diff -Nru pyglet-1.4.10/debian/changelog pyglet-1.5.14/debian/changelog --- pyglet-1.4.10/debian/changelog 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/changelog 2021-01-29 12:29:13.000000000 +0000 @@ -1,3 +1,35 @@ +pyglet (1.5.14-1) unstable; urgency=medium + + * Team upload. + * New upstream version + * Document ignoring interactive tests + * watch file standard 4 (routine-update) + + -- Andreas Tille Fri, 29 Jan 2021 13:29:13 +0100 + +pyglet (1.4.10-2) unstable; urgency=medium + + * Team upload + + [ Ondřej Nový ] + * d/control: Update Maintainer field with new Debian Python Team + contact address. + * d/control: Update Vcs-* fields with new Debian Python Team Salsa + layout. + + [ Michael R. Crusoe ] + * debian/rules: better skip of the interactive tests + * Standards-Version: 4.5.1 (routine-update) + * debhelper-compat 13 (routine-update) + * Testsuite: autopkgtest-pkg-python (routine-update) + * Remove trailing whitespace in debian/copyright (routine-update) + * Add salsa-ci file (routine-update) + * Rules-Requires-Root: no (routine-update) + * No tab in license text (routine-update) + * debian/docs: don't ship the nonexistant docs directory + + -- Michael R. Crusoe Fri, 29 Jan 2021 09:49:46 +0100 + pyglet (1.4.10-1) unstable; urgency=medium [ Ondřej Nový ] diff -Nru pyglet-1.4.10/debian/control pyglet-1.5.14/debian/control --- pyglet-1.4.10/debian/control 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/control 2021-01-29 12:29:13.000000000 +0000 @@ -1,12 +1,13 @@ Source: pyglet Section: python +Testsuite: autopkgtest-pkg-python Priority: optional -Maintainer: Debian Python Modules Team +Maintainer: Debian Python Team Uploaders: Michael Hanke , Per B. Sederberg , Stephan Peijnik , Yaroslav Halchenko -Build-Depends: debhelper-compat (= 9), +Build-Depends: debhelper-compat (= 13), dh-python, libavcodec-dev, libavformat-dev, libavutil-dev, libfreetype6-dev, @@ -30,10 +31,11 @@ python3-pytest, xauth, xvfb -Standards-Version: 4.4.1 +Standards-Version: 4.5.1 Homepage: http://www.pyglet.org -Vcs-Git: https://salsa.debian.org/python-team/modules/pyglet.git -Vcs-Browser: https://salsa.debian.org/python-team/modules/pyglet +Rules-Requires-Root: no +Vcs-Git: https://salsa.debian.org/python-team/packages/pyglet.git +Vcs-Browser: https://salsa.debian.org/python-team/packages/pyglet Package: python3-pyglet Architecture: all diff -Nru pyglet-1.4.10/debian/copyright pyglet-1.5.14/debian/copyright --- pyglet-1.4.10/debian/copyright 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/copyright 2021-01-29 12:29:13.000000000 +0000 @@ -95,10 +95,10 @@ Files: debian/* Copyright: 2007, Per B. Sederberg - 2007-2008, Michael Hanke - 2008, Ondrej Certik , - 2008, Stephan Peijnik - 2010, Yaroslav Halchenko + 2007-2008, Michael Hanke + 2008, Ondrej Certik , + 2008, Stephan Peijnik + 2010, Yaroslav Halchenko License: GPL On Debian GNU/Linux systems, the complete text of the GPL License can be found in `/usr/share/common-licenses/GPL'. @@ -452,4 +452,4 @@ DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE - OF THIS SOFTWARE. + OF THIS SOFTWARE. diff -Nru pyglet-1.4.10/debian/docs pyglet-1.5.14/debian/docs --- pyglet-1.4.10/debian/docs 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/docs 2021-01-29 12:29:13.000000000 +0000 @@ -1,3 +1,2 @@ README.md -doc/* tools diff -Nru pyglet-1.4.10/debian/patches/0003-Handle-py2-3-string-types.patch pyglet-1.5.14/debian/patches/0003-Handle-py2-3-string-types.patch --- pyglet-1.4.10/debian/patches/0003-Handle-py2-3-string-types.patch 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/patches/0003-Handle-py2-3-string-types.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -From: Mark Hymers -Date: Sat, 27 Jul 2019 15:07:07 +0100 -Subject: Handle py2/3 string types - -This fixes test case failrues in test_resource_loading.py ---- - pyglet/resource.py | 8 +++++++- - 1 file changed, 7 insertions(+), 1 deletion(-) - ---- a/pyglet/resource.py -+++ b/pyglet/resource.py -@@ -89,6 +89,12 @@ from future import standard_library - standard_library.install_aliases() - from builtins import object, str - -+# Handle python2/3 simultanously -+try: -+ from builtins import basestring -+except ImportError: -+ basestring = str -+ - __docformat__ = 'restructuredtext' - __version__ = '$Id: $' - -@@ -320,7 +326,7 @@ class Loader(object): - """ - if path is None: - path = ['.'] -- if isinstance(path, str): -+ if isinstance(path, basestring): - path = [path] - self.path = list(path) - self._script_home = script_home or get_script_home() diff -Nru pyglet-1.4.10/debian/patches/0004-Don-t-ship-copy-of-extlibs-in-the-binary-packages.patch pyglet-1.5.14/debian/patches/0004-Don-t-ship-copy-of-extlibs-in-the-binary-packages.patch --- pyglet-1.4.10/debian/patches/0004-Don-t-ship-copy-of-extlibs-in-the-binary-packages.patch 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/patches/0004-Don-t-ship-copy-of-extlibs-in-the-binary-packages.patch 2021-01-29 12:29:13.000000000 +0000 @@ -10,15 +10,7 @@ --- a/pyglet/image/codecs/png.py +++ b/pyglet/image/codecs/png.py -@@ -36,13 +36,15 @@ - """Encoder and decoder for PNG files, using PyPNG (png.py). - """ - -+from __future__ import absolute_import -+ - import array - import itertools - +@@ -42,7 +42,7 @@ import itertools from pyglet.image import * from pyglet.image.codecs import * @@ -27,15 +19,3 @@ class PNGImageDecoder(ImageDecoder): ---- a/setup.py -+++ b/setup.py -@@ -15,8 +15,7 @@ with open('pyglet/__init__.py') as f: - is_wheel = 'bdist_wheel' in sys.argv - - excluded = [] --if is_wheel: -- excluded.append('extlibs.future') -+excluded.append('extlibs') - - - def exclude_package(pkg): diff -Nru pyglet-1.4.10/debian/patches/series pyglet-1.5.14/debian/patches/series --- pyglet-1.4.10/debian/patches/series 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/patches/series 2021-01-29 12:29:13.000000000 +0000 @@ -1,2 +1 @@ -0003-Handle-py2-3-string-types.patch 0004-Don-t-ship-copy-of-extlibs-in-the-binary-packages.patch diff -Nru pyglet-1.4.10/debian/rules pyglet-1.5.14/debian/rules --- pyglet-1.4.10/debian/rules 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/rules 2021-01-29 12:29:13.000000000 +0000 @@ -26,9 +26,13 @@ # cf. #929697 EXCLUDED_TESTS += and not ClockTimingTestCase +# Exclude interactive tests which are mostly just stressing the OpenGL drivers (which is not very useful in this scenario) +# see https://github.com/pyglet/pyglet/issues/346#issuecomment-769692934 +EXCLUDED_TEST_GROUPS = --ignore=tests/interactive + export PYBUILD_NAME=pyglet export PYBUILD_TEST_PYTEST=1 -export PYBUILD_TEST_ARGS=>/dev/null 2>&1; cd {build_dir}; xvfb-run $(XVFB_OPTS) python{version} -m pytest -v -k "$(EXCLUDED_TESTS)" +export PYBUILD_TEST_ARGS=>/dev/null 2>&1; cd {build_dir}; xvfb-run $(XVFB_OPTS) python{version} -m pytest -v -k "$(EXCLUDED_TESTS)" $(EXCLUDED_TEST_GROUPS) %: dh $@ --with python3 --buildsystem=pybuild diff -Nru pyglet-1.4.10/debian/salsa-ci.yml pyglet-1.5.14/debian/salsa-ci.yml --- pyglet-1.4.10/debian/salsa-ci.yml 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/debian/salsa-ci.yml 2021-01-29 12:29:13.000000000 +0000 @@ -0,0 +1,4 @@ +--- +include: + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml + - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml diff -Nru pyglet-1.4.10/debian/watch pyglet-1.5.14/debian/watch --- pyglet-1.4.10/debian/watch 2020-02-03 06:55:30.000000000 +0000 +++ pyglet-1.5.14/debian/watch 2021-01-29 12:29:13.000000000 +0000 @@ -1,2 +1,2 @@ -version=3 +version=4 https://pypi.debian.net/pyglet/pyglet-([\d\.]+)@ARCHIVE_EXT@ diff -Nru pyglet-1.4.10/examples/apple_remote_demo.py pyglet-1.5.14/examples/apple_remote_demo.py --- pyglet-1.4.10/examples/apple_remote_demo.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/apple_remote_demo.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,229 +0,0 @@ -"""A silly demonstration of how to use the Apple remote. -""" - -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import pyglet -from pyglet.gl import * -import sys - - -class MainWindow(pyglet.window.Window): - def __init__(self): - super(MainWindow, self).__init__(visible=False) - self.set_caption('Apple Remote Example') - - # Look for the Apple Remote device. - remote = pyglet.input.get_apple_remote() - if not remote: - print('Apple IR Remote not available.') - sys.exit(0) - - # Open the remote in exclusive mode so that pressing the remote - # buttons does not activate Front Row, change volume, etc. while - # the remote is being used by our program. - remote.open(self, exclusive=True) - - # We push this class onto the remote's event handler stack so that - # the on_button_press and on_button_release methods which we define - # below will be called for the appropriate remote events. - remote.push_handlers(self) - - self.carousel = Carousel() - self.setup_opengl() - pyglet.clock.schedule_interval(self.update, 1 / 60.0) - - # Event handler for Apple Remote button press events. - # The button parameter is a string specifying the button that was pressed. - def on_button_press(self, button): - print('on_button_press', button) - - if button == 'up': - self.carousel.scroll_up() - elif button == 'down': - self.carousel.scroll_down() - elif button == 'left': - self.carousel.step_left() - elif button == 'right': - self.carousel.step_right() - elif button == 'left_hold': - self.carousel.rotate_left() - elif button == 'right_hold': - self.carousel.rotate_right() - elif button == 'select' or button == 'select_hold': - self.carousel.swap_left() - elif button == 'menu' or button == 'menu_hold': - self.carousel.swap_right() - - # Event handler for Apple Remote button release events. - # The button parameter is a string specifying the button that was released. - def on_button_release(self, button): - print('on_button_release', button) - - if button == 'left_hold': - self.carousel.stop_rotating() - elif button == 'right_hold': - self.carousel.stop_rotating() - - def on_draw(self): - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - gluLookAt(0, 3, -12, 0, 3, 0, 0, 1, 0) - self.carousel.draw() - - def on_resize(self, width, height): - glViewport(0, 0, width, height) - glMatrixMode(GL_PROJECTION) - glLoadIdentity() - aspect = width / float(height) - glFrustum(-1, 1, -1.8 / aspect, 0.2 / aspect, 1, 100) - glMatrixMode(GL_MODELVIEW) - return pyglet.event.EVENT_HANDLED - - def setup_opengl(self): - glClearColor(1, 1, 1, 1) - glEnable(GL_DEPTH_TEST) - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - def update(self, dt): - self.carousel.update(dt) - - -class Carousel: - """A rotating collection of labeled tiles.""" - - def __init__(self): - self.num_tiles = 14 - self.index = 0 - self.float_index = 0.0 - self.float_increment = 1.0 / self.num_tiles - self.angle = 0 - self.index_diff = 0 - self.is_rotating = False - self.speed = 4 * self.num_tiles - - # Create the tiles in the carousel. - self.tiles = [] - colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 205, 205), - (128, 0, 128), (255, 165, 0)] - - class Tile: - value = 0 - color = [255, 255, 255] - - for i in range(self.num_tiles): - tile = Tile() - tile.value = i % 26 - tile.color = colors[i % len(colors)] - self.tiles.append(tile) - - # Create glyphs for the characters displayed on the tiles. - font = pyglet.font.load('Courier', 64) - self.glyphs = font.get_glyphs('ABCDEFGHIJKLMNOPQRSTUVWXYZ') - - def scroll_up(self): - """Increment the character displayed on the main tile.""" - self.tiles[self.index].value = (self.tiles[self.index].value + 1) % 26 - - def scroll_down(self): - """Decrement the character displayed on the main tile.""" - self.tiles[self.index].value = (self.tiles[self.index].value - 1) % 26 - - def swap_left(self): - """Swap the two left tiles.""" - i = self.index - j = (self.index - 1) % self.num_tiles - self.tiles[i], self.tiles[j] = self.tiles[j], self.tiles[i] - - def swap_right(self): - """Swap the two right tiles.""" - i = self.index - j = (self.index + 1) % self.num_tiles - self.tiles[i], self.tiles[j] = self.tiles[j], self.tiles[i] - - def step_left(self): - """Rotate the carousel one tile to the left.""" - self.direction = -1 - self.index_diff += 1.0 - - def step_right(self): - """Rotate the carousel one tile to the right.""" - self.direction = 1 - self.index_diff += 1.0 - - def rotate_left(self): - """Start the carousel rotating continuously to the left.""" - self.is_rotating = True - self.direction = -1 - - def rotate_right(self): - """Start the carousel rotating continuously to the right.""" - self.is_rotating = True - self.direction = 1 - - def stop_rotating(self): - """Stop continuous rotation and make sure we end up at a tile location.""" - self.index_diff = round(self.float_index) - self.float_index - if self.index_diff < 0: - self.direction = -1 - else: - self.direction = 1 - self.index_diff = abs(self.index_diff) - - def draw(self): - glPushMatrix() - glRotatef(-self.angle, 0, 1, 0) - for i in range(self.num_tiles): - self.draw_tile(i) - glPopMatrix() - - def draw_tile(self, index): - angle = index * (360.0 / self.num_tiles) - - glPushMatrix() - glRotatef(angle, 0, 1, 0) - glTranslatef(0, 0, -7.5) - glRotatef(-angle + self.angle, 0, 1, 0) - - texture = self.glyphs[self.tiles[index].value].texture - vertex_list = pyglet.graphics.vertex_list(4, 'v2f', ('t3f', texture.tex_coords)) - vertex_list.vertices[:] = [-1, -1, 1, -1, 1, 1, -1, 1] - # Draw tile background. - glColor3ub(*self.tiles[index].color) - vertex_list.draw(GL_QUADS) - # Draw tile label. - glBindTexture(texture.target, texture.id) - glEnable(texture.target) - glColor3ub(0, 0, 0) - vertex_list.vertices[:] = [.8, -.8, -.8, -.8, -.8, .8, .8, .8] - glTranslatef(0, 0, -.01) - vertex_list.draw(GL_QUADS) - glDisable(texture.target) - glPopMatrix() - - def update(self, dt): - if self.is_rotating or self.index_diff: - increment = self.direction * self.speed * self.float_increment * dt - self.float_index = (self.float_index + increment) % self.num_tiles - - if self.index_diff: - self.index_diff -= abs(increment) - if self.index_diff < 0: - self.index_diff = 0 - self.float_index = round(self.float_index) % self.num_tiles - self.index = int(self.float_index) - self.is_rotating = False - - self.angle = (self.float_index / self.num_tiles) * 360 - - -if __name__ == '__main__': - window = MainWindow() - window.clear() - window.flip() - window.set_visible(True) - pyglet.app.run() diff -Nru pyglet-1.4.10/examples/apple_remote.py pyglet-1.5.14/examples/apple_remote.py --- pyglet-1.4.10/examples/apple_remote.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/apple_remote.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -#!/usr/bin/env python - -"""Print all Apple Remote events to stdout. -""" - -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import pyglet -import sys - -window = pyglet.window.Window() - - -@window.event -def on_draw(): - window.clear() - - -remote = pyglet.input.get_apple_remote() -if not remote: - print('Apple IR Remote not available.') - sys.exit(0) - -remote.open(window, exclusive=True) - - -@remote.select_control.event -def on_press(): - print('Press select') - - -@remote.menu_control.event -def on_press(): - print('Press menu') - - -@remote.up_control.event -def on_press(): - print('Press up') - - -@remote.down_control.event -def on_press(): - print('Press down') - - -@remote.left_control.event -def on_press(): - print('Press left') - - -@remote.right_control.event -def on_press(): - print('Press right') - - -@remote.select_control.event -def on_release(): - print('Release select') - - -@remote.menu_control.event -def on_release(): - print('Release menu') - - -@remote.up_control.event -def on_release(): - print('Release up') - - -@remote.down_control.event -def on_release(): - print('Release down') - - -@remote.left_control.event -def on_release(): - print('Release left') - - -@remote.right_control.event -def on_release(): - print('Release right') - - -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/astraea/astraea.py pyglet-1.5.14/examples/astraea/astraea.py --- pyglet-1.4.10/examples/astraea/astraea.py 2020-01-17 18:30:46.000000000 +0000 +++ pyglet-1.5.14/examples/astraea/astraea.py 2020-11-16 14:49:52.000000000 +0000 @@ -42,7 +42,6 @@ Space: Shoot """ -import sys import math import random @@ -143,7 +142,7 @@ yield x, y -class AsteroidSize(object): +class AsteroidSize: def __init__(self, filename, points): self.img = resource.image(filename) center_anchor(self.img) @@ -273,7 +272,7 @@ animations.remove(self) -class Starfield(object): +class Starfield: def __init__(self, img): self.x = 0 self.y = 0 @@ -302,7 +301,7 @@ # Overlays, such as menus and "Game Over" banners # -------------------------------------------------------------------------- -class Overlay(object): +class Overlay: def update(self, dt): pass @@ -369,7 +368,7 @@ item.draw(i == self.selected_index) -class MenuItem(object): +class MenuItem: pointer_color = (.46, 0, 1.) inverted_pointers = False @@ -480,7 +479,7 @@ self.items.append(MenuItem('Instructions', 200, begin_instructions_menu)) self.items.append(MenuItem('Options', 160, begin_options_menu)) - self.items.append(MenuItem('Quit', 120, sys.exit)) + self.items.append(MenuItem('Quit', 120, win.close)) self.reset() @@ -760,7 +759,7 @@ resume_game() return True elif symbol == key.ESCAPE: - sys.exit() + win.close() return pyglet.event.EVENT_HANDLED diff -Nru pyglet-1.4.10/examples/camera.py pyglet-1.5.14/examples/camera.py --- pyglet-1.4.10/examples/camera.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/examples/camera.py 2020-11-18 13:03:44.000000000 +0000 @@ -6,9 +6,9 @@ zoom when rendering. For example, you might have a playfield that needs to scroll and/or zoom, and a GUI layer that will remain static. For that scenario, you can create two Camera instances. You can optionally set -the maximum allowed zoom, and scrolling speed:: +the minimum allowed zoom, maximum allowed zoom, and scrolling speed:: - world_camera = Camera(scroll_speed=5, max_zoom=4) + world_camera = Camera(scroll_speed=5, min_zoom=1, max_zoom=4) gui_camera = Camera() After creating Camera instances, the zoom can be easily updated. It will @@ -58,12 +58,14 @@ class Camera: """ A simple 2D camera that contains the speed and offset.""" - def __init__(self, scroll_speed=1, max_zoom=4): + def __init__(self, scroll_speed=1, min_zoom=1, max_zoom=4): + assert min_zoom <= max_zoom, "Minimum zoom must not be greater than maximum zoom" self.scroll_speed = scroll_speed self.max_zoom = max_zoom + self.min_zoom = min_zoom self.offset_x = 0 self.offset_y = 0 - self._zoom = 1 + self._zoom = max(min(1, self.max_zoom), self.min_zoom) @property def zoom(self): @@ -71,8 +73,8 @@ @zoom.setter def zoom(self, value): - """ Here we set zoom, clamp value to minimum of 1 and max of max_zoom.""" - self._zoom = max(min(value, self.max_zoom), 1) + """ Here we set zoom, clamp value to minimum of min_zoom and max of max_zoom.""" + self._zoom = max(min(value, self.max_zoom), self.min_zoom) @property def position(self): @@ -114,3 +116,27 @@ def __exit__(self, exception_type, exception_value, traceback): self.end() + + +class CenteredCamera(Camera): + """A simple 2D camera class. 0, 0 will be the centre of the screen, as opposed to the bottom left.""" + + def __init__(self, window: pyglet.window.Window, *args, **kwargs): + self.window = window + super().__init__(*args, **kwargs) + + def begin(self): + x = -self.window.width//2/self._zoom + self.offset_x + y = -self.window.height//2/self._zoom + self.offset_y + + pyglet.gl.glTranslatef(-x * self._zoom, -y * self._zoom, 0) + + pyglet.gl.glScalef(self._zoom, self._zoom, 1) + + def end(self): + x = -self.window.width//2/self._zoom + self.offset_x + y = -self.window.height//2/self._zoom + self.offset_y + + pyglet.gl.glScalef(1 / self._zoom, 1 / self._zoom, 1) + + pyglet.gl.glTranslatef(x * self._zoom, y * self._zoom, 0) diff -Nru pyglet-1.4.10/examples/events/register_event_type.py pyglet-1.5.14/examples/events/register_event_type.py --- pyglet-1.4.10/examples/events/register_event_type.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/events/register_event_type.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,49 @@ +""" +This example demonstrates an alternative way to register an event type. +The original way looks like this: + + class Foo(EventDispatcher): + # ... + + + Foo.register_event_type('bar') + +Instead of calling the classmethod register_event_type() below the implementation of your dispatcher, +you can decorate the latter with the function decorator: + + @register_event_type('bar') + class Foo(EventDispatcher): + # ... + +All in one place. +""" + +import pyglet +from pyglet.event import EventDispatcher + + +def register_event_type(name): + def _reg_evt_type(cls): + assert issubclass(cls, EventDispatcher), "Event types can only be registered on EventDispatcher subclasses" + if not hasattr(cls, 'event_types'): + cls.event_types = [] + + cls.event_types.append(name) + return cls + + return _reg_evt_type + + +def create_alarm(dt): + alarms.dispatch_event('on_wake_up', dt) + + +@register_event_type('on_wake_up') +class Alarms(EventDispatcher): + def on_wake_up(self, dt): + print('Yet another {} seconds wasted! Wake up!'.format(dt)) + + +alarms = Alarms() +pyglet.clock.schedule_interval(create_alarm, 30) +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/events/window_events.py pyglet-1.5.14/examples/events/window_events.py --- pyglet-1.4.10/examples/events/window_events.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/events/window_events.py 2020-11-24 22:36:48.000000000 +0000 @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Prints all window events to stdout. +""" + +import pyglet + +window = pyglet.window.Window(resizable=True) + + +@window.event +def on_draw(): + window.clear() + + +# A logger class, which prints events to stdout or to a file: +win_event_logger = pyglet.window.event.WindowEventLogger() +window.push_handlers(win_event_logger) + +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/events/window_platform_event.py pyglet-1.5.14/examples/events/window_platform_event.py --- pyglet-1.4.10/examples/events/window_platform_event.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/events/window_platform_event.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,87 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Demonstrates how to handle a platform-specific event not defined in +pyglet by subclassing Window. This is not for the faint-hearted! + +A message will be printed to stdout when the following events are caught: + + - On Mac OS X, the window drag region is clicked. + - On Windows, the display resolution is changed. + - On Linux, the window properties are changed. + +""" + +from __future__ import print_function + +import pyglet + + +_have_cocoa = _have_win32 = _have_xlib = False + +if pyglet.compat_platform.startswith('linux'): + _have_xlib = True + from pyglet.window.xlib import * + +elif pyglet.compat_platform == 'darwin': + _have_cocoa = True + from pyglet.window.cocoa import * + +elif pyglet.compat_platform in ('win32', 'cygwin'): + _have_win32 = True + from pyglet.window.win32 import * + + +# Subclass Window +class MyWindow(pyglet.window.Window): + if _have_cocoa: + # TODO This is currently not supported in Cocoa (#156). + pass + + if _have_win32: + @Win32EventHandler(WM_DISPLAYCHANGE) + def _on_window_display_change(self, msg, lParam, wParam): + print('Display resolution changed.') + return 0 + + if _have_xlib: + @XlibEventHandler(xlib.PropertyNotify) + def _on_window_property_notify(self, event): + print('Property notify.') + + +if __name__ == '__main__': + window = MyWindow() + pyglet.app.run() diff -Nru pyglet-1.4.10/examples/fixed_resolution.py pyglet-1.5.14/examples/fixed_resolution.py --- pyglet-1.4.10/examples/fixed_resolution.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/fixed_resolution.py 2020-11-24 21:43:18.000000000 +0000 @@ -52,109 +52,125 @@ from pyglet.gl import * import pyglet -# Create a fullscreen window using the user's desktop resolution. You can -# also use this technique on ordinary resizable windows. -window = pyglet.window.Window(fullscreen=True) - -# Use 320x200 fixed resolution to make the effect completely obvious. You -# can change this to a more reasonable value such as 800x600 here. -target_resolution = 320, 200 -class FixedResolutionViewport(object): +class FixedResolution: def __init__(self, window, width, height, filtered=False): self.window = window self.width = width self.height = height - # Get the actual framebuffer size as this can be different from the window size - self.framebuffer_width, self.framebuffer_height = self.window.get_framebuffer_size() - self.texture = pyglet.image.Texture.create(width, height, - rectangle=True) + self._filtered = filtered + self._viewport = 0, 0, 0, 0, 0 + self._calculate_viewport(self.window.width, self.window.height) + self._cam_x = 0 + self._cam_y = 0 + self.clear_color = 0, 0, 0, 1 + + self.texture = pyglet.image.Texture.create(width, height, rectangle=True) if not filtered: - # By default the texture will be bilinear filtered when scaled - # up. If requested, turn filtering off. This makes the image - # aliased, but is more suitable for pixel art. - glTexParameteri(self.texture.target, - GL_TEXTURE_MAG_FILTER, GL_NEAREST) - glTexParameteri(self.texture.target, - GL_TEXTURE_MIN_FILTER, GL_NEAREST) - - def begin(self): + pyglet.image.Texture.default_min_filter = GL_NEAREST + pyglet.image.Texture.default_mag_filter = GL_NEAREST + glTexParameteri(self.texture.target, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + glTexParameteri(self.texture.target, GL_TEXTURE_MIN_FILTER, GL_NEAREST) + + def on_resize(w, h): + self._calculate_viewport(w, h) + self.window_w, self.window_h = w, h + + self.window.on_resize = on_resize + + def _calculate_viewport(self, new_screen_width, new_screen_height): + aspect_ratio = self.width / self.height + aspect_width = new_screen_width + aspect_height = aspect_width / aspect_ratio + 0.5 + if aspect_height > new_screen_height: + aspect_height = new_screen_height + aspect_width = aspect_height * aspect_ratio + 0.5 + + if not self._filtered: + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST) + + self._viewport = (int((new_screen_width / 2) - (aspect_width / 2)), # x + int((new_screen_height / 2) - (aspect_height / 2)), # y + 0, # z + int(aspect_width), # width + int(aspect_height)) # height + + def __enter__(self): glViewport(0, 0, self.width, self.height) - self.set_fixed_projection() + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + glOrtho(0, self.width, 0, self.height, -255, 255) + glMatrixMode(GL_MODELVIEW) + glTranslatef(self._cam_x, self._cam_y, 0) - def end(self): + def set_camera(self, x=0, y=0): + self._cam_x = -x + self._cam_y = -y + + def __exit__(self, *unused): + win = self.window buffer = pyglet.image.get_buffer_manager().get_color_buffer() self.texture.blit_into(buffer, 0, 0, 0) - glViewport(0, 0, self.framebuffer_width, self.framebuffer_height) - self.set_window_projection() - - aspect_width = self.window.width / float(self.width) - aspect_height = self.window.height / float(self.height) - - if aspect_width > aspect_height: - scale_width = aspect_height * self.width - scale_height = aspect_height * self.height - else: - scale_width = aspect_width * self.width - scale_height = aspect_width * self.height - - x = (self.window.width - scale_width) / 2 - y = (self.window.height - scale_height) / 2 - - glClearColor(0, 0, 0, 1) - glClear(GL_COLOR_BUFFER_BIT) - glLoadIdentity() - glColor3f(1, 1, 1) - self.texture.blit(x, y, width=scale_width, height=scale_height) - - def set_fixed_projection(self): - # Override this method if you need to change the projection of the - # fixed resolution viewport. + glViewport(0, 0, win.width, win.height) glMatrixMode(GL_PROJECTION) glLoadIdentity() - glOrtho(0, self.width, 0, self.height, -1, 1) + glOrtho(0, win.width, 0, win.height, -1, 1) glMatrixMode(GL_MODELVIEW) - def set_window_projection(self): - # This is the same as the default window projection, reprinted here - # for clarity. - glMatrixMode(GL_PROJECTION) + glClearColor(*self.clear_color) + glClear(GL_COLOR_BUFFER_BIT) glLoadIdentity() - glOrtho(0, self.window.width, 0, self.window.height, -1, 1) - glMatrixMode(GL_MODELVIEW) -target_width, target_height = target_resolution -viewport = FixedResolutionViewport(window, - target_width, target_height, filtered=False) - -def draw_scene(): - '''Draw the scene, assuming the fixed resolution viewport and projection - have been set up. This just draws the rotated polygon.''' - glClearColor(1, 1, 1, 1) - glClear(GL_COLOR_BUFFER_BIT) - - glLoadIdentity() - w, h = target_resolution - glTranslatef(w//2, h//2, 0) - glRotatef(rotate, 0, 0, 1) - glColor3f(1, 0, 0) - s = min(w, h) // 3 - glRectf(-s, -s, s, s) + self.texture.blit(*self._viewport) + + def begin(self): + self.__enter__() + + def end(self): + self.__exit__() + + +################################### +# Simple program using the Viewport: +################################### + +window = pyglet.window.Window(960, 540, resizable=True) +# Use 320x180 fixed resolution to make the effect completely obvious. You +# can change this to a more reasonable value such as 960x540 here: +target_width, target_height = 320, 180 + +# Create an instance of the FixedResolution class: +viewport = FixedResolution(window, target_width, target_height, filtered=False) + -rotate = 0 def update(dt): - global rotate - rotate += dt * 20 -pyglet.clock.schedule_interval(update, 1/60.) + global rectangle + rectangle.rotation += dt * 10 + @window.event def on_draw(): - viewport.begin() - window.clear() - draw_scene() - viewport.end() + # The viewport can be used as + # a context manager: + with viewport: + window.clear() + rectangle.draw() + + # # Alternatively, you can do it manually: + # viewport.begin() + # window.clear() + # rectangle.draw() + # viewport.end() + + +# Create a simple Rectangle to show the effect +rectangle = pyglet.shapes.Rectangle(x=target_width/2, y=target_height/2, color=(200, 0, 0), width=100, height=100) +rectangle.anchor_position = 50, 50 +# Schedule the update function at 60fps +pyglet.clock.schedule_interval(update, 1/60) pyglet.app.run() diff -Nru pyglet-1.4.10/examples/font_comparison.py pyglet-1.5.14/examples/font_comparison.py --- pyglet-1.4.10/examples/font_comparison.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/font_comparison.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,157 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""A simple tool that may be used to explore font faces. (Windows only) - -Only the fonts installed in the system are visible. - -Use the left/right cursor keys to change font faces. - -By default only the pyglet safe fonts are shown, toggle the safe flag -to see all. - -Don't include tabs in the text sample (see -http://pyglet.org/doc-current/programming_guide/text.html#id9 ) -""" - -from __future__ import print_function, unicode_literals - -import pyglet -import pyglet.font.win32query as wq - - -# support to generate a sample text good to spot monospace compliance. -# Chosen to do a table of fields_per_line columns, each column with field_size -# characters. Fields are filled with a rolling subset of ASCII characters. -class SampleTable(object): - field_size = 7 - gap_size = 3 - fields_per_line = 7 - spaces = ' ' * field_size - max_chars_per_line = (field_size + gap_size) * fields_per_line - gap_size - - def __init__(self): - self.lines = [] - self.current_line = '' - - def newline(self): - self.lines.append(self.current_line) - self.current_line = '' - - def add_field(self, s): - assert len(s) <= self.field_size - to_add = self.spaces[len(s):] + s - if self.current_line: - to_add = ' ' * self.gap_size + to_add - if len(self.current_line) + len(to_add) > self.max_chars_per_line: - self.newline() - self.add_field(s) - else: - self.current_line = self.current_line + to_add - - def text(self): - return '\n'.join(self.lines) - - -def sample_text_monospaced_table(): - printables = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' - table = SampleTable() - for i in range(6): - s = printables[i:] + printables[:i] - for k in range(0, len(printables), table.field_size): - table.add_field(s[k:k + table.field_size]) - table.newline() - return table.text() - - -# this worked right with all fonts in a win xp installation -def pyglet_safe(fontentry): - """ this is heuristic and conservative. YMMV. """ - return fontentry.vector and fontentry.family != wq.FF_DONTCARE - - -class Window(pyglet.window.Window): - font_num = 0 - - def on_text_motion(self, motion): - if motion == pyglet.window.key.MOTION_RIGHT: - self.font_num += 1 - if self.font_num == len(font_names): - self.font_num = 0 - elif motion == pyglet.window.key.MOTION_LEFT: - self.font_num -= 1 - if self.font_num < 0: - self.font_num = len(font_names) - 1 - - face = font_names[self.font_num] - self.head = pyglet.text.Label(face, font_size=16, y=0, anchor_y='bottom') - self.text = pyglet.text.Label(sample_text, font_name=face, font_size=12, - y=self.height, anchor_y='top', width=self.width, - multiline=True) - - def on_draw(self): - self.clear() - self.head.draw() - self.text.draw() - - -lorem_ipsum = """ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. -Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec -consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget -libero egestas mattis sit amet vitae augue. - - -""" - -if __name__ == '__main__': - print(__doc__) - safe = True - sample_text = lorem_ipsum + sample_text_monospaced_table() - # all fonts known by the OS - fontdb = wq.query() - - if safe: - candidates = [f for f in fontdb if pyglet_safe(f)] - else: - canditates = fontdb - - # theres one fontentry for each charset supported, so reduce names - font_names = list(set([f.name for f in candidates])) - - font_names.sort() - window = Window(1024, 600) - window.on_text_motion(None) - pyglet.app.run() diff -Nru pyglet-1.4.10/examples/graphics/image_convert.py pyglet-1.5.14/examples/graphics/image_convert.py --- pyglet-1.4.10/examples/graphics/image_convert.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/graphics/image_convert.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Convert an image to another file format supported by pyglet. + +Usage:: + python image_convert.py + +""" + +from __future__ import print_function + +import sys + +import pyglet + + +def convert(src, dest): + if '.dds' in src.lower(): + # Compressed textures need to be uploaded to the video card before + # they can be saved. + texture = pyglet.image.load(src).get_texture() + texture.save(dest) + else: + # Otherwise just save the loaded image in the new format. + image = pyglet.image.load(src) + image.save(dest) + + +if __name__ == '__main__': + if len(sys.argv) != 3: + print(__doc__) + sys.exit(1) + + src = sys.argv[1] + dest = sys.argv[2] + convert(src, dest) diff -Nru pyglet-1.4.10/examples/graphics/image_display.py pyglet-1.5.14/examples/graphics/image_display.py --- pyglet-1.4.10/examples/graphics/image_display.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/graphics/image_display.py 2020-11-20 14:57:26.000000000 +0000 @@ -0,0 +1,83 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Display an image. + +Usage:: + + display.py + +A checkerboard background is visible behind any transparent areas. +""" + +from __future__ import print_function + +import sys + +import pyglet +from pyglet.gl import * + +window = pyglet.window.Window(visible=False, resizable=True) + + +@window.event +def on_draw(): + background.blit_tiled(0, 0, 0, window.width, window.height) + img.blit(window.width // 2, window.height // 2, 0) + + +if __name__ == '__main__': + if len(sys.argv) != 2: + print(__doc__) + sys.exit(1) + + filename = sys.argv[1] + + img = pyglet.image.load(filename).get_texture(rectangle=True) + img.anchor_x = img.width // 2 + img.anchor_y = img.height // 2 + + checks = pyglet.image.create(32, 32, pyglet.image.CheckerImagePattern()) + background = pyglet.image.TileableTexture.create_for_image(checks) + + # Enable alpha blending, required for image.blit. + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + + window.width = img.width + window.height = img.height + window.set_visible() + + pyglet.app.run() Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/gui/bar.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/gui/bar.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/gui/button_down.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/gui/button_down.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/gui/button_hover.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/gui/button_hover.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/gui/button_up.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/gui/button_up.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/gui/knob.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/gui/knob.png differ diff -Nru pyglet-1.4.10/examples/gui/widgets.py pyglet-1.5.14/examples/gui/widgets.py --- pyglet-1.4.10/examples/gui/widgets.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/gui/widgets.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,82 @@ +import pyglet + +window = pyglet.window.Window(540, 500, caption="Widget Example") +batch = pyglet.graphics.Batch() +pyglet.gl.glClearColor(0.8, 0.8, 0.8, 1.0) + + +@window.event +def on_draw(): + window.clear() + batch.draw() + + +#################################### +# load resources to use for Widgets: +#################################### + +depressed = pyglet.resource.image('button_up.png') +pressed = pyglet.resource.image('button_down.png') +hover = pyglet.resource.image('button_hover.png') +bar = pyglet.resource.image('bar.png') +knob = pyglet.resource.image('knob.png') + + +###################################### +# Create some event handler functions: +###################################### + +def slider_handler(value): + slider_label.text = f"Slider Value: {round(value, 1)}" + + +def toggle_button_handler(value): + toggle_label.text = f"Toggle Button: {value}" + + +def push_button_handler(): + push_label.text = f"Push Button: True" + + +def release_button_handler(): + push_label.text = f"Push Button: False" + + +def text_entry_handler(text): + text_entry_label.text = f"Text: {text}" + + +############################### +# Create some Widget instances: +############################### + +# A Frame instance to hold all Widgets: +frame = pyglet.gui.Frame(window, order=4) + + +togglebutton = pyglet.gui.ToggleButton(100, 400, pressed=pressed, depressed=depressed, hover=hover, batch=batch) +togglebutton.set_handler('on_toggle', toggle_button_handler) +frame.add_widget(togglebutton) +toggle_label = pyglet.text.Label("Toggle Button: False", x=300, y=400, batch=batch, color=(0, 0, 0, 255)) + + +pushbutton = pyglet.gui.PushButton(100, 300, pressed=pressed, depressed=depressed, hover=hover, batch=batch) +pushbutton.set_handler('on_press', push_button_handler) +pushbutton.set_handler('on_release', release_button_handler) +frame.add_widget(pushbutton) +push_label = pyglet.text.Label("Push Button: False", x=300, y=300, batch=batch, color=(0, 0, 0, 255)) + + +slider = pyglet.gui.Slider(100, 200, bar, knob, edge=5, batch=batch) +slider.set_handler('on_change', slider_handler) +frame.add_widget(slider) +slider_label = pyglet.text.Label("Slider Value: 0.0", x=300, y=200, batch=batch, color=(0, 0, 0, 255)) + + +text_entry = pyglet.gui.TextEntry("Enter Your Name", 100, 100, 150, batch=batch) +frame.add_widget(text_entry) +text_entry.set_handler('on_commit', text_entry_handler) +text_entry_label = pyglet.text.Label("Text: None", x=300, y=100, batch=batch, color=(0, 0, 0, 255)) + + +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/hello_world.py pyglet-1.5.14/examples/hello_world.py --- pyglet-1.4.10/examples/hello_world.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/hello_world.py 2020-11-23 22:35:58.000000000 +0000 @@ -1,9 +1,7 @@ -#!/usr/bin/env python import pyglet window = pyglet.window.Window() label = pyglet.text.Label('Hello, world!', - font_name='Arial', font_size=36, x=window.width // 2, y=window.height // 2, diff -Nru pyglet-1.4.10/examples/html_label.py pyglet-1.5.14/examples/html_label.py --- pyglet-1.4.10/examples/html_label.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/html_label.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""A simple demonstration of the HTMLLabel class, as it might be used on a -help or introductory screen. -""" - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import os -import pyglet - -html = ''' -

HTML labels in pyglet

- -

- -

HTML labels are a simple way to add formatted text to your application. -Different fonts, styles -and colours are supported. - -

This window has been made resizable; text will reflow to fit the new size. -''' - -window = pyglet.window.Window(resizable=True) -location = pyglet.resource.FileLocation(os.path.dirname(__file__)) -label = pyglet.text.HTMLLabel(html, location=location, - width=window.width, - multiline=True, anchor_y='center') - - -@window.event -def on_resize(width, height): - # Wrap text to the width of the window - label.width = window.width - - # Keep text vertically centered in the window - label.y = window.height // 2 - - -@window.event -def on_draw(): - window.clear() - label.draw() - - -pyglet.gl.glClearColor(1, 1, 1, 1) -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/image_convert.py pyglet-1.5.14/examples/image_convert.py --- pyglet-1.4.10/examples/image_convert.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/image_convert.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""Convert an image to another file format supported by pyglet. - -Usage:: - python image_convert.py - -""" - -from __future__ import print_function - -import sys - -import pyglet - - -def convert(src, dest): - if '.dds' in src.lower(): - # Compressed textures need to be uploaded to the video card before - # they can be saved. - texture = pyglet.image.load(src).get_texture() - texture.save(dest) - else: - # Otherwise just save the loaded image in the new format. - image = pyglet.image.load(src) - image.save(dest) - - -if __name__ == '__main__': - if len(sys.argv) != 3: - print(__doc__) - sys.exit(1) - - src = sys.argv[1] - dest = sys.argv[2] - convert(src, dest) diff -Nru pyglet-1.4.10/examples/image_display.py pyglet-1.5.14/examples/image_display.py --- pyglet-1.4.10/examples/image_display.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/image_display.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""Display an image. - -Usage:: - - display.py - -A checkerboard background is visible behind any transparent areas. -""" - -from __future__ import print_function - -import sys - -import pyglet -from pyglet.gl import * - -window = pyglet.window.Window(visible=False, resizable=True) - - -@window.event -def on_draw(): - background.blit_tiled(0, 0, 0, window.width, window.height) - img.blit(window.width // 2, window.height // 2, 0) - - -if __name__ == '__main__': - if len(sys.argv) != 2: - print(__doc__) - sys.exit(1) - - filename = sys.argv[1] - - img = pyglet.image.load(filename).get_texture(rectangle=True) - img.anchor_x = img.width // 2 - img.anchor_y = img.height // 2 - - checks = pyglet.image.create(32, 32, pyglet.image.CheckerImagePattern()) - background = pyglet.image.TileableTexture.create_for_image(checks) - - # Enable alpha blending, required for image.blit. - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - window.width = img.width - window.height = img.height - window.set_visible() - - pyglet.app.run() diff -Nru pyglet-1.4.10/examples/input/apple_remote_demo.py pyglet-1.5.14/examples/input/apple_remote_demo.py --- pyglet-1.4.10/examples/input/apple_remote_demo.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/input/apple_remote_demo.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,229 @@ +"""A silly demonstration of how to use the Apple remote. +""" + +from __future__ import print_function + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import pyglet +from pyglet.gl import * +import sys + + +class MainWindow(pyglet.window.Window): + def __init__(self): + super(MainWindow, self).__init__(visible=False) + self.set_caption('Apple Remote Example') + + # Look for the Apple Remote device. + remote = pyglet.input.get_apple_remote() + if not remote: + print('Apple IR Remote not available.') + sys.exit(0) + + # Open the remote in exclusive mode so that pressing the remote + # buttons does not activate Front Row, change volume, etc. while + # the remote is being used by our program. + remote.open(self, exclusive=True) + + # We push this class onto the remote's event handler stack so that + # the on_button_press and on_button_release methods which we define + # below will be called for the appropriate remote events. + remote.push_handlers(self) + + self.carousel = Carousel() + self.setup_opengl() + pyglet.clock.schedule_interval(self.update, 1 / 60.0) + + # Event handler for Apple Remote button press events. + # The button parameter is a string specifying the button that was pressed. + def on_button_press(self, button): + print('on_button_press', button) + + if button == 'up': + self.carousel.scroll_up() + elif button == 'down': + self.carousel.scroll_down() + elif button == 'left': + self.carousel.step_left() + elif button == 'right': + self.carousel.step_right() + elif button == 'left_hold': + self.carousel.rotate_left() + elif button == 'right_hold': + self.carousel.rotate_right() + elif button == 'select' or button == 'select_hold': + self.carousel.swap_left() + elif button == 'menu' or button == 'menu_hold': + self.carousel.swap_right() + + # Event handler for Apple Remote button release events. + # The button parameter is a string specifying the button that was released. + def on_button_release(self, button): + print('on_button_release', button) + + if button == 'left_hold': + self.carousel.stop_rotating() + elif button == 'right_hold': + self.carousel.stop_rotating() + + def on_draw(self): + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + gluLookAt(0, 3, -12, 0, 3, 0, 0, 1, 0) + self.carousel.draw() + + def on_resize(self, width, height): + glViewport(0, 0, width, height) + glMatrixMode(GL_PROJECTION) + glLoadIdentity() + aspect = width / float(height) + glFrustum(-1, 1, -1.8 / aspect, 0.2 / aspect, 1, 100) + glMatrixMode(GL_MODELVIEW) + return pyglet.event.EVENT_HANDLED + + def setup_opengl(self): + glClearColor(1, 1, 1, 1) + glEnable(GL_DEPTH_TEST) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + + def update(self, dt): + self.carousel.update(dt) + + +class Carousel: + """A rotating collection of labeled tiles.""" + + def __init__(self): + self.num_tiles = 14 + self.index = 0 + self.float_index = 0.0 + self.float_increment = 1.0 / self.num_tiles + self.angle = 0 + self.index_diff = 0 + self.is_rotating = False + self.speed = 4 * self.num_tiles + + # Create the tiles in the carousel. + self.tiles = [] + colors = [(255, 0, 0), (0, 255, 0), (0, 0, 255), (255, 255, 0), (0, 205, 205), + (128, 0, 128), (255, 165, 0)] + + class Tile: + value = 0 + color = [255, 255, 255] + + for i in range(self.num_tiles): + tile = Tile() + tile.value = i % 26 + tile.color = colors[i % len(colors)] + self.tiles.append(tile) + + # Create glyphs for the characters displayed on the tiles. + font = pyglet.font.load('Courier', 64) + self.glyphs = font.get_glyphs('ABCDEFGHIJKLMNOPQRSTUVWXYZ') + + def scroll_up(self): + """Increment the character displayed on the main tile.""" + self.tiles[self.index].value = (self.tiles[self.index].value + 1) % 26 + + def scroll_down(self): + """Decrement the character displayed on the main tile.""" + self.tiles[self.index].value = (self.tiles[self.index].value - 1) % 26 + + def swap_left(self): + """Swap the two left tiles.""" + i = self.index + j = (self.index - 1) % self.num_tiles + self.tiles[i], self.tiles[j] = self.tiles[j], self.tiles[i] + + def swap_right(self): + """Swap the two right tiles.""" + i = self.index + j = (self.index + 1) % self.num_tiles + self.tiles[i], self.tiles[j] = self.tiles[j], self.tiles[i] + + def step_left(self): + """Rotate the carousel one tile to the left.""" + self.direction = -1 + self.index_diff += 1.0 + + def step_right(self): + """Rotate the carousel one tile to the right.""" + self.direction = 1 + self.index_diff += 1.0 + + def rotate_left(self): + """Start the carousel rotating continuously to the left.""" + self.is_rotating = True + self.direction = -1 + + def rotate_right(self): + """Start the carousel rotating continuously to the right.""" + self.is_rotating = True + self.direction = 1 + + def stop_rotating(self): + """Stop continuous rotation and make sure we end up at a tile location.""" + self.index_diff = round(self.float_index) - self.float_index + if self.index_diff < 0: + self.direction = -1 + else: + self.direction = 1 + self.index_diff = abs(self.index_diff) + + def draw(self): + glPushMatrix() + glRotatef(-self.angle, 0, 1, 0) + for i in range(self.num_tiles): + self.draw_tile(i) + glPopMatrix() + + def draw_tile(self, index): + angle = index * (360.0 / self.num_tiles) + + glPushMatrix() + glRotatef(angle, 0, 1, 0) + glTranslatef(0, 0, -7.5) + glRotatef(-angle + self.angle, 0, 1, 0) + + texture = self.glyphs[self.tiles[index].value].texture + vertex_list = pyglet.graphics.vertex_list(4, 'v2f', ('t3f', texture.tex_coords)) + vertex_list.vertices[:] = [-1, -1, 1, -1, 1, 1, -1, 1] + # Draw tile background. + glColor3ub(*self.tiles[index].color) + vertex_list.draw(GL_QUADS) + # Draw tile label. + glBindTexture(texture.target, texture.id) + glEnable(texture.target) + glColor3ub(0, 0, 0) + vertex_list.vertices[:] = [.8, -.8, -.8, -.8, -.8, .8, .8, .8] + glTranslatef(0, 0, -.01) + vertex_list.draw(GL_QUADS) + glDisable(texture.target) + glPopMatrix() + + def update(self, dt): + if self.is_rotating or self.index_diff: + increment = self.direction * self.speed * self.float_increment * dt + self.float_index = (self.float_index + increment) % self.num_tiles + + if self.index_diff: + self.index_diff -= abs(increment) + if self.index_diff < 0: + self.index_diff = 0 + self.float_index = round(self.float_index) % self.num_tiles + self.index = int(self.float_index) + self.is_rotating = False + + self.angle = (self.float_index / self.num_tiles) * 360 + + +if __name__ == '__main__': + window = MainWindow() + window.clear() + window.flip() + window.set_visible(True) + pyglet.app.run() diff -Nru pyglet-1.4.10/examples/input/apple_remote.py pyglet-1.5.14/examples/input/apple_remote.py --- pyglet-1.4.10/examples/input/apple_remote.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/input/apple_remote.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,90 @@ +#!/usr/bin/env python + +"""Print all Apple Remote events to stdout. +""" + +from __future__ import print_function + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import pyglet +import sys + +window = pyglet.window.Window() + + +@window.event +def on_draw(): + window.clear() + + +remote = pyglet.input.get_apple_remote() +if not remote: + print('Apple IR Remote not available.') + sys.exit(0) + +remote.open(window, exclusive=True) + + +@remote.select_control.event +def on_press(): + print('Press select') + + +@remote.menu_control.event +def on_press(): + print('Press menu') + + +@remote.up_control.event +def on_press(): + print('Press up') + + +@remote.down_control.event +def on_press(): + print('Press down') + + +@remote.left_control.event +def on_press(): + print('Press left') + + +@remote.right_control.event +def on_press(): + print('Press right') + + +@remote.select_control.event +def on_release(): + print('Release select') + + +@remote.menu_control.event +def on_release(): + print('Release menu') + + +@remote.up_control.event +def on_release(): + print('Release up') + + +@remote.down_control.event +def on_release(): + print('Release down') + + +@remote.left_control.event +def on_release(): + print('Release left') + + +@remote.right_control.event +def on_release(): + print('Release right') + + +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/input/input.py pyglet-1.5.14/examples/input/input.py --- pyglet-1.4.10/examples/input/input.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/input/input.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,46 @@ +#!/usr/bin/env python + +"""Print the details of all available input devices to stdout. +""" + +from __future__ import print_function + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import pyglet + +window = pyglet.window.Window() +devices = pyglet.input.get_devices() + + +def watch_control(device, control): + @control.event + def on_change(value): + print('%r: %r.on_change(%r)' % (device, control, value)) + + if isinstance(control, pyglet.input.base.Button): + @control.event + def on_press(): + print('%r: %r.on_press()' % (device, control)) + + @control.event + def on_release(): + print('%r: %r.on_release()' % (device, control)) + + +print('Devices:') +for device in devices: + print(' ', device.name, end=' ') + try: + device.open(window=window) + print('OK') + + for control in device.get_controls(): + print(' ', control.name) + watch_control(device, control) + + except pyglet.input.DeviceException: + print('Fail') + +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/input/joystick.py pyglet-1.5.14/examples/input/joystick.py --- pyglet-1.4.10/examples/input/joystick.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/input/joystick.py 2020-11-18 14:04:10.000000000 +0000 @@ -0,0 +1,96 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +import pyglet +from pyglet.gl import * + +joysticks = pyglet.input.get_joysticks() +assert joysticks, 'No joystick device is connected' +joystick = joysticks[0] +joystick.open() + +window = pyglet.window.Window() +main_batch = pyglet.graphics.Batch() + +# Labels +pyglet.text.Label("Buttons:", x=15, y=window.height - 25, + font_size=14, batch=main_batch) +pyglet.text.Label("D Pad:", x=window.width - 125, y=window.height - 25, + font_size=14, batch=main_batch) +rows = len(joystick.buttons) // 2 +buttton_labels = [] +for i in range(len(joystick.buttons)): + y = window.height - 50 - 25 * (i % rows) + x = 35 + 60 * (i // rows) + label = pyglet.text.Label(f"{i}:", x=x, y=y, font_size=14, + anchor_x='right', batch=main_batch) + buttton_labels.append(label) + + +@window.event +def on_draw(): + window.clear() + main_batch.draw() + x = round((.5 * joystick.x + 1), 2) * window.width / 2 + y = round((-.5 * joystick.y + 1), 2) * window.height / 2 + rx = (.5 * joystick.rx + 1) * 60 + ry = (-.5 * joystick.ry + 1) * 60 + z = joystick.z * 50 + + # Axes + joystick_rect = pyglet.shapes.Rectangle(x, y, 10 + rx + z, 10 + ry + z, color=(255, 0, 255)) + joystick_rect.anchor_x = joystick_rect.width // 2 + joystick_rect.anchor_y = joystick_rect.height // 2 + joystick_rect.draw() + + # Buttons + for i in range(len(joystick.buttons)): + x = buttton_labels[i].x + y = buttton_labels[i].y + rect = pyglet.shapes.Rectangle(x + 10, y + 1, 10, 10, color=(255, 0, 0)) + if joystick.buttons[i]: + rect.color = (0, 255, 0) + rect.draw() + + # Hat + x = window.width - 75 + y = window.height - 100 + d_pad_rect = pyglet.shapes.Rectangle(x + joystick.hat_x * 50, y + joystick.hat_y * 50, 10, 10) + d_pad_rect.color = (0, 0, 255) + d_pad_rect.draw() + + +pyglet.clock.schedule(lambda dt: None) +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/input/show_input.py pyglet-1.5.14/examples/input/show_input.py --- pyglet-1.4.10/examples/input/show_input.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/input/show_input.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,402 @@ +#!/usr/bin/env python + +"""Graphically show all devices available via the pyglet.input interface. + +Each device is shown in its own collapsed panel. Click on a device panel +to expand it, revealing that device's controls. The controls show the +current live values, and flash white when the value changes. +""" + +from __future__ import print_function + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import pyglet +from pyglet import gl + + +class LineGroup(pyglet.graphics.OrderedGroup): + def set_state(self): + gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE) + + def unset_state(self): + gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) + + +class Box: + def __init__(self, batch, group=None, + stroke_color=(255, 255, 255, 255), + fill_color=(200, 200, 200, 255)): + self.x1 = 0 + self.y1 = 0 + self.x2 = 0 + self.y2 = 0 + + self.fill_vertices = batch.add(4, gl.GL_QUADS, + pyglet.graphics.OrderedGroup(0, group), + 'v2f', ('c4B', fill_color * 4)) + self.stroke_vertices = batch.add(4, gl.GL_QUADS, + LineGroup(1, group), + 'v2f', ('c4B', stroke_color * 4)) + + def set_bounds(self, x1, y1, x2, y2): + self.x1 = 0 + self.y1 = 0 + self.x2 = 0 + self.y2 = 0 + self.fill_vertices.vertices[:] = (x1, y1, x2, y1, x2, y2, x1, y2) + self.stroke_vertices.vertices[:] = (x1, y1, x2, y1, x2, y2, x1 - 1, y2) + + def set_fill(self, r, g, b): + self.fill_vertices.colors[:] = (r, g, b, 255) * 4 + + def delete(self): + self.fill_vertices.delete() + self.stroke_vertices.delete() + + +class DevicePanel: + BORDER_MARGIN = 5 + CONTENT_MARGIN = 8 + + def __init__(self, device): + self.device = device + + self.box = Box(batch, group=background_group, + stroke_color=(0, 0, 200, 255), + fill_color=(200, 200, 255, 255)) + self.name_label = pyglet.text.Label(device.name or 'Unknown device', + font_size=10, + color=(0, 0, 0, 255), + anchor_y='top', + batch=batch, group=text_group) + self.manufacturer_label = pyglet.text.Label(device.manufacturer or '', + font_size=10, + color=(0, 0, 0, 255), anchor_x='right', + anchor_y='top', + batch=batch, group=text_group) + + self.is_open = False + self.widgets = [] + + def set_bounds(self, left, right, top): + self.left = left + self.right = right + self.top = top + self.layout() + + def layout_widgets(self): + max_row_width = self.right - self.left - self.CONTENT_MARGIN * 2 + + row = [] + row_width = 0 + row_height = 0 + + def layout_row(row, x1, y1, x2, y2): + x = x1 + for widget in row: + widget.set_bounds(x, + y1, + x + widget.min_width, + y1 + widget.min_height) + x += widget.min_width + + y = self.bottom + self.CONTENT_MARGIN + for widget in self.widgets: + if widget is None or row_width + widget.min_width > max_row_width: + layout_row(row, + self.left + self.CONTENT_MARGIN, + y - row_height, + self.right - self.CONTENT_MARGIN, + y) + row = [] + y -= row_height + row_width = 0 + + if widget is None: + break + + row.append(widget) + row_width += widget.min_width + row_height = max(row_height, widget.min_height) + + self.bottom = y - self.CONTENT_MARGIN + + def layout(self): + self.title_bottom = self.top - \ + self.name_label.content_height - self.CONTENT_MARGIN * 2 + self.bottom = self.title_bottom + if self.is_open: + self.layout_widgets() + + self.box.set_bounds(self.left + self.BORDER_MARGIN, + self.bottom + self.BORDER_MARGIN, + self.right - self.BORDER_MARGIN, + self.top - self.BORDER_MARGIN) + + self.name_label.x = self.left + self.CONTENT_MARGIN + self.name_label.y = self.top - self.CONTENT_MARGIN + self.manufacturer_label.x = self.right - self.CONTENT_MARGIN + self.manufacturer_label.y = self.top - self.CONTENT_MARGIN + + def hit_test(self, x, y): + return self.left < x < self.right and self.title_bottom < y < self.top + + def toggle(self): + if self.is_open: + self.close() + else: + self.open() + + def open(self): + if self.is_open: + return + + try: + self.device.open() + except pyglet.input.DeviceException as e: + try: + self.device.open(window) + except pyglet.input.DeviceException as e: + print(e) # TODO show error + return + + window.set_mouse_cursor(window.get_system_mouse_cursor('wait')) + for control in self.device.get_controls(): + if isinstance(control, pyglet.input.Button): + widget = ButtonWidget(control, batch, group=text_group) + else: + widget = ControlWidget(control, batch, group=text_group) + self.widgets.append(widget) + + if not self.widgets: + self.widgets.append(NoControlsWidget(batch, group=text_group)) + + self.widgets.append(None) + window.set_mouse_cursor(None) + + self.is_open = True + + def close(self): + if not self.is_open: + return + + for widget in self.widgets: + if widget: + widget.delete() + del self.widgets[:] + + self.device.close() + + self.is_open = False + + +class ControlWidget: + BORDER_MARGIN = 2 + CONTENT_MARGIN = 4 + + def __init__(self, control, batch, group=None): + self.control_name = control.name + if not self.control_name: + self.control_name = control.raw_name + self.box = Box(batch, pyglet.graphics.OrderedGroup(0, group)) + self.name_label = pyglet.text.Label(self.control_name, + font_size=10, + anchor_x='left', + anchor_y='bottom', + color=(0, 0, 0, 255), + batch=batch, + group=pyglet.graphics.OrderedGroup(1, group)) + self.value_label = pyglet.text.Label(' ', + font_size=8, + anchor_x='right', + anchor_y='bottom', + color=(0, 0, 0, 255), + batch=batch, + group=pyglet.graphics.OrderedGroup(1, group)) + + self.min_width = \ + self.name_label.content_width + \ + self.value_label.content_width + self.CONTENT_MARGIN * 2 + self.min_height = self.name_label.content_height + self.CONTENT_MARGIN * 2 + + self.relative = isinstance(control, pyglet.input.RelativeAxis) + self.fade = 200 + + self.control = control + control.push_handlers(self) + + def set_bounds(self, x1, y1, x2, y2): + self.box.set_bounds( + x1 + self.BORDER_MARGIN, + y1 + self.BORDER_MARGIN, + x2 - self.BORDER_MARGIN, + y2 - self.BORDER_MARGIN) + self.name_label.x = x1 + self.CONTENT_MARGIN + self.name_label.y = y1 + self.CONTENT_MARGIN + self.value_label.x = x2 - self.CONTENT_MARGIN + self.value_label.y = y1 + self.CONTENT_MARGIN + + def delete(self): + if self in changed_widgets: + changed_widgets.remove(self) + self.control.remove_handlers(self) + self.name_label.delete() + self.value_label.delete() + self.box.delete() + + def on_change(self, value): + self.value = value + self.fade = 255 + changed_widgets.add(self) + + def update(self): + self.value_label.text = str(self.value) + if self.relative and self.value: + self.value = 0 + changed_widgets.add(self) + + self.box.set_fill(self.fade, self.fade, self.fade) + if self.fade > 200: + self.fade = max(200, self.fade - 10) + changed_widgets.add(self) + + +class ButtonWidget(ControlWidget): + BORDER_MARGIN = 2 + CONTENT_MARGIN = 4 + + def __init__(self, control, batch, group=None): + self.control_name = control.name + if not self.control_name: + self.control_name = control.raw_name + self.box = Box(batch, pyglet.graphics.OrderedGroup(0, group)) + self.name_label = pyglet.text.Label(self.control_name, + font_size=10, + anchor_x='center', + anchor_y='bottom', + color=(0, 0, 0, 255), + batch=batch, + group=pyglet.graphics.OrderedGroup(1, group)) + + self.min_width = self.name_label.content_width + self.CONTENT_MARGIN * 2 + self.min_height = self.name_label.content_height + self.CONTENT_MARGIN * 2 + + self.fade = 200 + + self.control = control + control.push_handlers(self) + + def set_bounds(self, x1, y1, x2, y2): + self.box.set_bounds( + x1 + self.BORDER_MARGIN, + y1 + self.BORDER_MARGIN, + x2 - self.BORDER_MARGIN, + y2 - self.BORDER_MARGIN) + self.name_label.x = (x1 + x2) // 2 + self.name_label.y = y1 + self.CONTENT_MARGIN + + def delete(self): + if self in changed_widgets: + changed_widgets.remove(self) + self.control.remove_handlers(self) + self.name_label.delete() + self.box.delete() + + def on_change(self, value): + self.value = value + if value: + self.fade = 255 + changed_widgets.add(self) + + def update(self): + self.box.set_fill(self.fade, self.fade, self.fade) + if not self.value and self.fade > 200: + self.fade = max(200, self.fade - 10) + changed_widgets.add(self) + + +class NoControlsWidget: + CONTENT_MARGIN = 4 + + def __init__(self, batch, group): + self.label = pyglet.text.Label('No controls on this device.', + font_size=10, + color=(0, 0, 0, 255), + anchor_y='bottom', + batch=batch, + group=group) + + self.min_width = self.label.content_width + self.CONTENT_MARGIN * 2 + self.min_height = self.label.content_height + self.CONTENT_MARGIN * 2 + + def set_bounds(self, x1, y1, x2, y2): + self.label.x = x1 + ControlWidget.CONTENT_MARGIN + self.label.y = y1 + ControlWidget.CONTENT_MARGIN + + def delete(self): + self.label.delete() + + +window = pyglet.window.Window(caption='Input Devices', resizable=True) +batch = pyglet.graphics.Batch() +background_group = pyglet.graphics.OrderedGroup(0) +text_group = pyglet.graphics.OrderedGroup(1) + +panels = [DevicePanel(device) for device in pyglet.input.get_devices()] +help_label = pyglet.text.Label( + 'Click on a device name to show or hide its controls.', + x=DevicePanel.CONTENT_MARGIN, + anchor_y='top', + font_size=10, + color=(255, 255, 255, 255), + batch=batch, + group=background_group) + + +def layout_panels(): + y = window.height + for panel in panels: + panel.set_bounds(left=0, right=window.width, top=y) + y = panel.bottom + help_label.y = y + + +@window.event +def on_draw(): + gl.glClearColor(0.3, 0.3, 0.4, 1.0) + window.clear() + batch.draw() + window.invalid = False + + +@window.event +def on_resize(width, height): + layout_panels() + window.invalid = True + return pyglet.event.EVENT_UNHANDLED + + +@window.event +def on_mouse_press(x, y, button, modifiers): + for panel in panels: + if panel.hit_test(x, y): + panel.toggle() + layout_panels() + window.invalid = True + + +changed_widgets = set() + + +def update(dt): + pending = list(changed_widgets) + changed_widgets.clear() + for widget in pending: + widget.update() + window.invalid = True + + +pyglet.clock.schedule_interval(update, 0.05) +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/input/tablet.py pyglet-1.5.14/examples/input/tablet.py --- pyglet-1.4.10/examples/input/tablet.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/input/tablet.py 2020-11-18 14:04:10.000000000 +0000 @@ -0,0 +1,63 @@ +#!/usr/bin/python +# $Id:$ + +from __future__ import print_function + +import pyglet + +window = pyglet.window.Window() +tablets = pyglet.input.get_tablets() +canvases = [] + +if tablets: + print('Tablets:') + for i, tablet in enumerate(tablets): + print(' (%d) %s' % (i + 1, tablet.name)) + print('Press number key to open corresponding tablet device.') +else: + print('No tablets found.') + + +@window.event +def on_text(text): + try: + index = int(text) - 1 + except ValueError: + return + + if not (0 <= index < len(tablets)): + return + + name = tablets[i].name + + try: + canvas = tablets[i].open(window) + except pyglet.input.DeviceException: + print('Failed to open tablet %d on window' % index) + + print('Opened %s' % name) + + @canvas.event + def on_enter(cursor): + print('%s: on_enter(%r)' % (name, cursor)) + + @canvas.event + def on_leave(cursor): + print('%s: on_leave(%r)' % (name, cursor)) + + @canvas.event + def on_motion(cursor, x, y, pressure): + print('%s: on_motion(%r, %r, %r, %r)' % (name, cursor, x, y, pressure)) + + +@window.event +def on_mouse_press(x, y, button, modifiers): + print('on_mouse_press(%r, %r, %r, %r' % (x, y, button, modifiers)) + + +@window.event +def on_mouse_release(x, y, button, modifiers): + print('on_mouse_release(%r, %r, %r, %r' % (x, y, button, modifiers)) + + +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/input.py pyglet-1.5.14/examples/input.py --- pyglet-1.4.10/examples/input.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/input.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -#!/usr/bin/env python - -"""Print the details of all available input devices to stdout. -""" - -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import pyglet - -window = pyglet.window.Window() -devices = pyglet.input.get_devices() - - -def watch_control(device, control): - @control.event - def on_change(value): - print('%r: %r.on_change(%r)' % (device, control, value)) - - if isinstance(control, pyglet.input.base.Button): - @control.event - def on_press(): - print('%r: %r.on_press()' % (device, control)) - - @control.event - def on_release(): - print('%r: %r.on_release()' % (device, control)) - - -print('Devices:') -for device in devices: - print(' ', device.name, end=' ') - try: - device.open(window=window) - print('OK') - - for control in device.get_controls(): - print(' ', control.name) - watch_control(device, control) - - except pyglet.input.DeviceException: - print('Fail') - -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/joystick.py pyglet-1.5.14/examples/joystick.py --- pyglet-1.4.10/examples/joystick.py 2019-07-10 21:01:26.000000000 +0000 +++ pyglet-1.5.14/examples/joystick.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -#!/usr/bin/env python - -''' -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import pyglet -from pyglet.gl import * - -joysticks = pyglet.input.get_joysticks() -assert joysticks, 'No joystick device is connected' -joystick = joysticks[0] -joystick.open() - -window = pyglet.window.Window() - -@window.event -def on_draw(): - x = (0.8*joystick.x + 1) * window.width / 2 - y = (-0.8*joystick.y + 1) * window.height / 2 - z = joystick.z - angle = joystick.rz * 180 - - # Axes - - glClear(GL_COLOR_BUFFER_BIT) - glColor3f(1, 0, 0) - glLoadIdentity() - glTranslatef(x, y, 0) - glScalef(1 + z, 1 + z, 1 + z) - glRotatef(-angle, 0, 0, 1) - glBegin(GL_TRIANGLES) - glVertex2f(-10, 0) - glVertex2f(0, 13) - glVertex2f(10, 0) - glEnd() - - # Buttons - - glLoadIdentity() - x = 10 - y = 10 - glPointSize(5) - glBegin(GL_POINTS) - for button in joystick.buttons: - if button: - glVertex2f(x, y) - x += 20 - glEnd() - - # Hat - - glColor3f(0, 0, 1) - x = window.width / 2 - y = window.height / 2 - glBegin(GL_POINTS) - glVertex2f(x + joystick.hat_x * 50, y + joystick.hat_y * 50) - glEnd() - -pyglet.clock.schedule(lambda dt: None) -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/media/media_info.py pyglet-1.5.14/examples/media/media_info.py --- pyglet-1.4.10/examples/media/media_info.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/media_info.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,91 @@ +#!/usr/bin/env python + +"""Print details of a media file that pyglet can open (requires FFmpeg). + +Usage:: + + media_info.py + +""" + +from __future__ import print_function + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import sys +import pyglet + + +def print_ffmpeg_info(): + from pyglet.media import have_ffmpeg + + if have_ffmpeg(): + from pyglet.media.codecs import ffmpeg + print('Using FFmpeg version {0}'.format(ffmpeg.get_version())) + else: + print('FFmpeg not available; required for media decoding.') + print('https://www.ffmpeg.org/download.html\n') + + +def print_source_info(source): + if source.info: + if source.info.title: + print('Title: %s' % source.info.title) + if source.info.album: + print('Album: %s' % source.info.album) + if source.info.author: + print('Author: %s' % source.info.author) + if source.info.year: + print('Year: %d' % source.info.year) + if source.info.track: + print('Track: %d' % source.info.track) + if source.info.genre: + print('Genre: %s' % source.info.genre) + if source.info.copyright: + print('Copyright: %s' % source.info.copyright) + if source.info.comment: + print('Comment: %s' % source.info.comment) + + if source.audio_format: + af = source.audio_format + print('Audio: %d channel(s), %d bits, %.02f Hz' % ( + af.channels, af.sample_size, af.sample_rate)) + + if source.video_format: + vf = source.video_format + if vf.frame_rate: + frame_rate = '%.02f' % vf.frame_rate + else: + frame_rate = 'unknown' + if vf.sample_aspect >= 1: + display_width = vf.sample_aspect * vf.width + display_height = vf.height + else: + display_width = vf.width + display_height = vf.sample_aspect / vf.height + print('Video: %dx%d at aspect %r (displays at %dx%d), %s fps' % ( + vf.width, vf.height, vf.sample_aspect, + display_width, display_height, frame_rate)) + + hours = int(source.duration / 3600) + minutes = int(source.duration / 60) % 60 + seconds = int(source.duration) % 60 + milliseconds = int(source.duration * 1000) % 1000 + print('Duration: %d:%02d:%02d.%03d' % (hours, minutes, seconds, milliseconds)) + + +if __name__ == '__main__': + if len(sys.argv) != 2: + print(__doc__) + print_ffmpeg_info() + sys.exit(1) + + print_ffmpeg_info() + + filename = sys.argv[1] + try: + source = pyglet.media.load(filename, streaming=True) + print_source_info(source) + except pyglet.media.MediaException: + print('Could not open %s' % filename) diff -Nru pyglet-1.4.10/examples/media/media_player.py pyglet-1.5.14/examples/media/media_player.py --- pyglet-1.4.10/examples/media/media_player.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/media_player.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,460 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- +""" +Usage + + media_player.py [options] [ ...] + +Plays the audio / video files listed as arguments, optionally +collecting debug info + +Options + --debug : saves sequence of internal state as a binary *.dbg + --outfile : filename to store the debug info, defaults to filename.dbg + +The raw data captured in the .dbg can be rendered as human readable +using the script report.py +""" + +from __future__ import print_function + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import os +import sys +import weakref + +from pyglet.gl import * +import pyglet +from pyglet.window import key + +pyglet.options['debug_media'] = False +# pyglet.options['audio'] = ('openal', 'pulse', 'silent') +from pyglet.media import buffered_logger as bl + + +def draw_rect(x, y, width, height): + glBegin(GL_LINE_LOOP) + glVertex2f(x, y) + glVertex2f(x + width, y) + glVertex2f(x + width, y + height) + glVertex2f(x, y + height) + glEnd() + + +class Control(pyglet.event.EventDispatcher): + x = y = 0 + width = height = 10 + + def __init__(self, parent): + super(Control, self).__init__() + self.parent = weakref.proxy(parent) + + def hit_test(self, x, y): + return (self.x < x < self.x + self.width and + self.y < y < self.y + self.height) + + def capture_events(self): + self.parent.push_handlers(self) + + def release_events(self): + self.parent.remove_handlers(self) + + +class Button(Control): + charged = False + + def draw(self): + if self.charged: + glColor3f(1, 0, 0) + draw_rect(self.x, self.y, self.width, self.height) + glColor3f(1, 1, 1) + self.draw_label() + + def on_mouse_press(self, x, y, button, modifiers): + self.capture_events() + self.charged = True + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + self.charged = self.hit_test(x, y) + + def on_mouse_release(self, x, y, button, modifiers): + self.release_events() + if self.hit_test(x, y): + self.dispatch_event('on_press') + self.charged = False + + +Button.register_event_type('on_press') + + +class TextButton(Button): + def __init__(self, *args, **kwargs): + super(TextButton, self).__init__(*args, **kwargs) + self._text = pyglet.text.Label('', anchor_x='center', anchor_y='center') + + def draw_label(self): + self._text.x = self.x + self.width / 2 + self._text.y = self.y + self.height / 2 + self._text.draw() + + def set_text(self, text): + self._text.text = text + + text = property(lambda self: self._text.text, + set_text) + + +class Slider(Control): + THUMB_WIDTH = 6 + THUMB_HEIGHT = 10 + GROOVE_HEIGHT = 2 + RESPONSIVNESS = 0.3 + + def __init__(self, *args, **kwargs): + super(Slider, self).__init__(*args, **kwargs) + self.seek_value = None + + def draw(self): + center_y = self.y + self.height / 2 + draw_rect(self.x, center_y - self.GROOVE_HEIGHT / 2, + self.width, self.GROOVE_HEIGHT) + pos = self.x + self.value * self.width / (self.max - self.min) + draw_rect(pos - self.THUMB_WIDTH / 2, center_y - self.THUMB_HEIGHT / 2, + self.THUMB_WIDTH, self.THUMB_HEIGHT) + + def coordinate_to_value(self, x): + value = float(x - self.x) / self.width * (self.max - self.min) + self.min + return value + + def on_mouse_press(self, x, y, button, modifiers): + value = self.coordinate_to_value(x) + self.capture_events() + self.dispatch_event('on_begin_scroll') + self.dispatch_event('on_change', value) + pyglet.clock.schedule_once(self.seek_request, self.RESPONSIVNESS) + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + # On some platforms, on_mouse_drag is triggered with a high frequency. + # Seeking takes some time (~200ms). Asking for a seek at every + # on_mouse_drag event would starve the event loop. + # Instead we only record the last mouse position and we + # schedule seek_request to dispatch the on_change event in the future. + # This will allow subsequent on_mouse_drag to change the seek_value + # without triggering yet the on_change event. + value = min(max(self.coordinate_to_value(x), self.min), self.max) + if self.seek_value is None: + # We have processed the last recorded mouse position. + # We re-schedule seek_request + pyglet.clock.schedule_once(self.seek_request, self.RESPONSIVNESS) + self.seek_value = value + + def on_mouse_release(self, x, y, button, modifiers): + self.release_events() + self.dispatch_event('on_end_scroll') + self.seek_value = None + + def seek_request(self, dt): + if self.seek_value is not None: + self.dispatch_event('on_change', self.seek_value) + self.seek_value = None + + +Slider.register_event_type('on_begin_scroll') +Slider.register_event_type('on_end_scroll') +Slider.register_event_type('on_change') + + +class PlayerWindow(pyglet.window.Window): + GUI_WIDTH = 400 + GUI_HEIGHT = 40 + GUI_PADDING = 4 + GUI_BUTTON_HEIGHT = 16 + + def __init__(self, player): + super(PlayerWindow, self).__init__(caption='Media Player', + visible=False, + resizable=True) + # We only keep a weakref to player as we are about to push ourself + # as a handler which would then create a circular reference between + # player and window. + self.player = weakref.proxy(player) + self._player_playing = False + self.player.push_handlers(self) + + self.slider = Slider(self) + self.slider.push_handlers(self) + self.slider.x = self.GUI_PADDING + self.slider.y = self.GUI_PADDING * 2 + self.GUI_BUTTON_HEIGHT + + self.play_pause_button = TextButton(self) + self.play_pause_button.x = self.GUI_PADDING + self.play_pause_button.y = self.GUI_PADDING + self.play_pause_button.height = self.GUI_BUTTON_HEIGHT + self.play_pause_button.width = 45 + self.play_pause_button.on_press = self.on_play_pause + + self.window_button = TextButton(self) + self.window_button.x = self.play_pause_button.x + \ + self.play_pause_button.width + self.GUI_PADDING + self.window_button.y = self.GUI_PADDING + self.window_button.height = self.GUI_BUTTON_HEIGHT + self.window_button.width = 90 + self.window_button.text = 'Windowed' + self.window_button.on_press = lambda: self.set_fullscreen(False) + + self.controls = [ + self.slider, + self.play_pause_button, + self.window_button, + ] + + x = self.window_button.x + self.window_button.width + self.GUI_PADDING + i = 0 + for screen in self.display.get_screens(): + screen_button = TextButton(self) + screen_button.x = x + screen_button.y = self.GUI_PADDING + screen_button.height = self.GUI_BUTTON_HEIGHT + screen_button.width = 80 + screen_button.text = 'Screen %d' % (i + 1) + screen_button.on_press = lambda screen=screen: self.set_fullscreen(True, screen) + self.controls.append(screen_button) + i += 1 + x += screen_button.width + self.GUI_PADDING + + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + + def on_player_next_source(self): + self.gui_update_state() + self.gui_update_source() + self.set_default_video_size() + return True + + def on_player_eos(self): + self.gui_update_state() + pyglet.clock.schedule_once(self.auto_close, 0.1) + return True + + def gui_update_source(self): + if self.player.source: + source = self.player.source + self.slider.min = 0. + self.slider.max = source.duration + self.gui_update_state() + + def gui_update_state(self): + if self.player.playing: + self.play_pause_button.text = 'Pause' + else: + self.play_pause_button.text = 'Play' + + def get_video_size(self): + if not self.player.source or not self.player.source.video_format: + return 0, 0 + video_format = self.player.source.video_format + width = video_format.width + height = video_format.height + if video_format.sample_aspect > 1: + width *= video_format.sample_aspect + elif video_format.sample_aspect < 1: + height /= video_format.sample_aspect + return width, height + + def set_default_video_size(self): + """Make the window size just big enough to show the current + video and the GUI.""" + width = self.GUI_WIDTH + height = self.GUI_HEIGHT + video_width, video_height = self.get_video_size() + width = max(width, video_width) + height += video_height + self.set_size(int(width), int(height)) + + def on_resize(self, width, height): + """Position and size video image.""" + super(PlayerWindow, self).on_resize(width, height) + self.slider.width = width - self.GUI_PADDING * 2 + + height -= self.GUI_HEIGHT + if height <= 0: + return + + video_width, video_height = self.get_video_size() + if video_width == 0 or video_height == 0: + return + display_aspect = width / float(height) + video_aspect = video_width / float(video_height) + if video_aspect > display_aspect: + self.video_width = width + self.video_height = width / video_aspect + else: + self.video_height = height + self.video_width = height * video_aspect + self.video_x = (width - self.video_width) / 2 + self.video_y = (height - self.video_height) / 2 + self.GUI_HEIGHT + + def on_mouse_press(self, x, y, button, modifiers): + for control in self.controls: + if control.hit_test(x, y): + control.on_mouse_press(x, y, button, modifiers) + + def on_key_press(self, symbol, modifiers): + if symbol == key.SPACE: + self.on_play_pause() + elif symbol == key.ESCAPE: + self.dispatch_event('on_close') + elif symbol == key.LEFT: + self.player.seek(0) + elif symbol == key.RIGHT: + self.player.next_source() + + def on_close(self): + self.player.pause() + self.close() + + def auto_close(self, dt): + self.close() + + def on_play_pause(self): + if self.player.playing: + self.player.pause() + else: + if self.player.time >= self.player.source.duration: + self.player.seek(0) + self.player.play() + self.gui_update_state() + + def on_draw(self): + self.clear() + + # Video + if self.player.source and self.player.source.video_format: + video_texture = self.player.texture + video_texture.blit(self.video_x, + self.video_y, + width=self.video_width, + height=self.video_height) + + # GUI + self.slider.value = self.player.time + for control in self.controls: + control.draw() + + def on_begin_scroll(self): + self._player_playing = self.player.playing + self.player.pause() + + def on_change(self, value): + self.player.seek(value) + + def on_end_scroll(self): + if self._player_playing: + self.player.play() + + +def main(target, dbg_file, debug): + set_logging_parameters(target, dbg_file, debug) + + player = pyglet.media.Player() + window = PlayerWindow(player) + + player.queue(pyglet.media.load(filename) for filename in sys.argv[1:]) + + window.gui_update_source() + window.set_visible(True) + window.set_default_video_size() + + # this is an async call + player.play() + window.gui_update_state() + + pyglet.app.run() + + +def set_logging_parameters(target_file, dbg_file, debug): + if not debug: + bl.logger = None + return + if dbg_file is None: + dbg_file = target_file + ".dbg" + else: + dbg_dir = os.path.dirname(dbg_file) + if dbg_dir and not os.path.isdir(dbg_dir): + os.mkdir(dbg_dir) + bl.logger = bl.BufferedLogger(dbg_file) + from pyglet.media.instrumentation import mp_events + # allow to detect crashes by prewriting a crash file, if no crash + # it will be overwrited by the captured data + sample = os.path.basename(target_file) + bl.logger.log("version", mp_events["version"]) + bl.logger.log("crash", sample) + bl.logger.save_log_entries_as_pickle() + bl.logger.clear() + # start the real capture data + bl.logger.log("version", mp_events["version"]) + bl.logger.log("mp.im", sample) + + +def usage(): + print(__doc__) + sys.exit(1) + + +def sysargs_to_mainargs(): + """builds main args from sys.argv""" + if len(sys.argv) < 2: + usage() + debug = False + dbg_file = None + for i in range(2): + if sys.argv[1].startswith("--"): + a = sys.argv.pop(1) + if a.startswith("--debug"): + debug = True + elif a.startswith("--outfile="): + dbg_file = a[len("--outfile="):] + else: + print("Error unknown option:", a) + usage() + target_file = sys.argv[1] + return target_file, dbg_file, debug + + +if __name__ == '__main__': + target_file, dbg_file, debug = sysargs_to_mainargs() + main(target_file, dbg_file, debug) Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/media/noisy/ball.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/media/noisy/ball.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/media/noisy/ball.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/media/noisy/ball.wav differ diff -Nru pyglet-1.4.10/examples/media/noisy/noisy.py pyglet-1.5.14/examples/media/noisy/noisy.py --- pyglet-1.4.10/examples/media/noisy/noisy.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/noisy/noisy.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,119 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Bounces balls around a window and plays noises. + +This is a simple demonstration of how pyglet efficiently manages many sound +channels without intervention. +""" + +import random +import sys + +from pyglet.gl import * +import pyglet +from pyglet.window import key + +BALL_IMAGE = 'ball.png' +BALL_SOUND = 'ball.wav' + +if len(sys.argv) > 1: + BALL_SOUND = sys.argv[1] + +window = pyglet.window.Window(640, 480) + +sound = pyglet.resource.media(BALL_SOUND, streaming=False) +balls_batch = pyglet.graphics.Batch() +balls = [] +label = pyglet.text.Label('Press space to add a ball, backspace to remove', + font_size=14, + x=window.width // 2, y=10, + anchor_x='center') + + +class Ball(pyglet.sprite.Sprite): + ball_image = pyglet.resource.image(BALL_IMAGE) + width = ball_image.width + height = ball_image.height + + def __init__(self): + x = random.random() * (window.width - self.width) + y = random.random() * (window.height - self.height) + + super(Ball, self).__init__(self.ball_image, x, y, batch=balls_batch) + + self.dx = (random.random() - 0.5) * 1000 + self.dy = (random.random() - 0.5) * 1000 + + def update_position(self, dt): + if self.x <= 0 or self.x + self.width >= window.width: + self.dx *= -1 + sound.play() + if self.y <= 0 or self.y + self.height >= window.height: + self.dy *= -1 + sound.play() + self.x += self.dx * dt + self.y += self.dy * dt + + self.x = min(max(self.x, 0), window.width - self.width) + self.y = min(max(self.y, 0), window.height - self.height) + + +@window.event +def on_key_press(symbol, modifiers): + if symbol == key.SPACE: + balls.append(Ball()) + elif symbol == key.BACKSPACE: + if balls: + del balls[-1] + elif symbol == key.ESCAPE: + window.has_exit = True + + +@window.event +def on_draw(): + window.clear() + balls_batch.draw() + label.draw() + + +def update(dt): + for ball in balls: + ball.update_position(dt) + + +if __name__ == '__main__': + pyglet.clock.schedule_interval(update, 1 / 30.) + pyglet.app.run() diff -Nru pyglet-1.4.10/examples/media/noisy/README pyglet-1.5.14/examples/media/noisy/README --- pyglet-1.4.10/examples/media/noisy/README 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/noisy/README 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,11 @@ +noisy +===== + +This is an example program that accompanies pyglet (http://www.pyglet.org). +Due to licensing restrictions on some of the assets, this game cannot be used +for commercial purposes. + +The source code is licensed under the BSD license, which is quite permissive +(see the source header for details). + +All artwork and the sound is Copyright 2007 Alex Holkner. diff -Nru pyglet-1.4.10/examples/media/soundspace/reader.py pyglet-1.5.14/examples/media/soundspace/reader.py --- pyglet-1.4.10/examples/media/soundspace/reader.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/soundspace/reader.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,113 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- +# $Id$ + +import os + +from pyglet import media + + +class ReaderException(Exception): + pass + + +class PlayerReader: + def __init__(self, player): + self.player = player + + def line(self, line, lineno): + parts = line.split() + if parts[0] == 'position': + if len(parts) < 4: + raise ReaderException('Invalid position line %d' % lineno) + self.player.position = tuple([float(x) for x in parts[1:]]) + if parts[0] == 'cone_orientation': + if len(parts) < 4: + raise ReaderException('Invalid orientation line %d' % lineno) + self.player.cone_orientation = tuple([float(x) for x in parts[1:]]) + elif parts[0] == 'outer_cone_angle': + if len(parts) < 2: + raise ReaderException('Invalid angle line %d' % lineno) + self.player.cone_outer_angle = float(parts[1]) + elif parts[0] == 'inner_cone_angle': + if len(parts) < 2: + raise ReaderException('Invalid angle line %d' % lineno) + self.player.cone_inner_angle = float(parts[1]) + elif parts[0] == 'label': + if len(parts) < 2: + raise ReaderException('Invalid label line %d' % lineno) + self.player.label = parts[1] + + +class SpaceReader: + def __init__(self, space): + self.basedir = '' + self.space = space + + def read(self, file): + if not hasattr(file, 'read'): + self.basedir = os.path.dirname(file) + file = open(file, 'rt') + elif hasattr(file, 'name'): + self.basedir = os.path.dirname(file.name) + reader = None + lineno = 0 + for line in file: + lineno += 1 + + if not isinstance('', bytes) and isinstance(line, bytes): + # decode bytes to str on Python 3 + line = line.decode('ascii') + + if not line.strip() or line.startswith('#'): + continue + if line.startswith(' '): + if not reader: + raise ReaderException('Unexpected indented block line %d' % lineno) + reader.line(line, lineno) + else: + reader = None + parts = line.split() + if parts[0] == 'loop': + if len(parts) < 2: + raise ReaderException('No loop filename line %d' % lineno) + player = media.Player() + player.loop = True + player.queue(self.source(parts[1], streaming=False)) + self.space.add_player(player) + reader = PlayerReader(player) + + def source(self, filename, **kwargs): + filename = os.path.join(self.basedir, filename) + return media.load(filename, **kwargs) diff -Nru pyglet-1.4.10/examples/media/soundspace/README pyglet-1.5.14/examples/media/soundspace/README --- pyglet-1.4.10/examples/media/soundspace/README 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/soundspace/README 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,38 @@ +Sound Space +=========== + +This is a toy program for playing with positional audio in pyglet +(http://www.pyglet.org). On Linux, OpenAL is required. + +The source code is licensed under the BSD license, which is quite permissive +(see the source headers for details). + +Audio samples were generated with Apple GarageBand. + +Usage +----- + +Run the toy with:: + + python soundspace.py + +Everything is controlled with the mouse. Hover over a control to +see its name. Click and drag an empty area to pan the view, and scroll the +scroll wheel to zoom in and out. + +The red triangles can be dragged to move the position of a player or listener. + +The dashed line to the yellow handle shows the cone orientation; this can be +reoriented by dragging it. + +The blue and green segments represent the outer and inner cone angles, +respectively. You can resize the angle by dragging the handle attached to the +cone. + +There is a master volume control beneath the listener. + +Click the (+) sign on a player to show the min_gain, max_gain, cone_outer_gain +and volume controls. + +The initial configuration is given in res/space.txt; it should be +self-explanatory (parsed by reader.py). Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/media/soundspace/res/bass.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/media/soundspace/res/bass.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/media/soundspace/res/drums.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/media/soundspace/res/drums.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/media/soundspace/res/guitar.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/media/soundspace/res/guitar.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/media/soundspace/res/piano.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/media/soundspace/res/piano.wav differ diff -Nru pyglet-1.4.10/examples/media/soundspace/res/space.txt pyglet-1.5.14/examples/media/soundspace/res/space.txt --- pyglet-1.4.10/examples/media/soundspace/res/space.txt 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/soundspace/res/space.txt 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,24 @@ +loop drums.wav + label Drums + position -1.5 0 4 + cone_orientation 1 0 -1 + outer_cone_angle 360 + inner_cone_angle 90 +loop piano.wav + label Piano + position 3 0 4.5 + cone_orientation -.5 0 -1 + outer_cone_angle 360 + inner_cone_angle 90 +loop bass.wav + label Bass + position -3 0 -4 + cone_orientation .5 0 1 + outer_cone_angle 270 + inner_cone_angle 90 +loop guitar.wav + label Guitar + position 5 0 -3 + cone_orientation -1.5 0 1 + outer_cone_angle 270 + inner_cone_angle 90 diff -Nru pyglet-1.4.10/examples/media/soundspace/soundspace.py pyglet-1.5.14/examples/media/soundspace/soundspace.py --- pyglet-1.4.10/examples/media/soundspace/soundspace.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/soundspace/soundspace.py 2020-12-22 09:03:54.000000000 +0000 @@ -0,0 +1,623 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- +# $Id$ + +import math + +import pyglet +from pyglet.gl import * + +import reader + +pyglet.resource.path.append('res') +pyglet.resource.reindex() + +# Default to OpenAL if available: +pyglet.options['audio'] = 'openal', 'pulse', 'directsound', 'silent' + + +def disc(r, x, y, slices=20, start=0, end=2*math.pi): + d = (end - start) / (slices - 1) + s = start + points = [(x, y)] + [(x + r * math.cos(a*d+s), y + r * math.sin(a*d+s)) + for a in range(slices)] + points = ((GLfloat * 2) * len(points))(*points) + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) + glEnableClientState(GL_VERTEX_ARRAY) + glVertexPointer(2, GL_FLOAT, 0, points) + glDrawArrays(GL_TRIANGLE_FAN, 0, len(points)) + glPopClientAttrib() + + +def circle(r, x, y, slices=20): + d = 2 * math.pi / slices + points = [(x + r * math.cos(a*d), y + r * math.sin(a*d)) for a in range(slices)] + points = ((GLfloat * 2) * len(points))(*points) + glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) + glEnableClientState(GL_VERTEX_ARRAY) + glVertexPointer(2, GL_FLOAT, 0, points) + glDrawArrays(GL_LINE_LOOP, 0, len(points)) + glPopClientAttrib() + + +def orientation_angle(orientation): + return math.atan2(orientation[2], orientation[0]) + + +class Handle: + tip = '' + + def __init__(self, player): + self.player = player + + def hit_test(self, x, y, z): + dx, dy, dz = [a - b for a, b in zip(self.pos(), (x, y, z))] + if dx * dx + dy * dy + dz * dz < self.radius * self.radius: + return -dx, -dy, -dz + + def draw(self): + pass + + def begin_drag(self, window, offset): + self.win = window + self.offset = offset + return self + + def on_mouse_press(self, x, y, button, modifiers): + self.win.remove_handlers(self) + + def on_mouse_release(self, x, y, button, modifiers): + self.win.remove_handlers(self) + + +class LabelHandle(Handle): + def __init__(self, player): + super(LabelHandle, self).__init__(player) + self.text = pyglet.text.Label('', font_size=10, color=(0, 0, 0, 255), + anchor_y='top', anchor_x='center') + + def hit_test(self, x, y, z): + return None + + def draw(self): + if hasattr(self.player, 'label'): + x, _, y = self.player.position + + # ech. fudge scale back to 1 + mat = (GLfloat * 16)() + glGetFloatv(GL_MODELVIEW_MATRIX, mat) + + glPushMatrix() + glTranslatef(x, y, 0) + glScalef(1/mat[0], 1/mat[5], 1/mat[10]) + glTranslatef(0, -5, 0) + + self.text.text = self.player.label + self.text.draw() + + glPopMatrix() + + +class PositionHandle(Handle): + tip = 'position' + radius = .3 + + def draw(self): + glPushMatrix() + glTranslatef(self.player.position[0], self.player.position[2], 0) + glColor3f(1, 0, 0) + glBegin(GL_TRIANGLES) + glVertex2f(0, self.radius) + glVertex2f(-self.radius * math.sqrt(3) / 2, -.5 * self.radius) + glVertex2f(self.radius * math.sqrt(3) / 2, -.5 * self.radius) + glEnd() + glPopMatrix() + + def pos(self): + return self.player.position + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + pos = self.win.mouse_transform(x, y) + self.player.position = \ + (pos[0] - self.offset[0], + pos[1] - self.offset[1], + pos[2] - self.offset[2]) + + +class OrientationHandle(Handle): + radius = .1 + length = 1.5 + + def pos(self): + x, _, z = self.player.position + direction = self.get_orientation() + sz = math.sqrt(direction[0] ** 2 + direction[1] ** 2 + direction[2] ** 2) or 1 + if sz != 0: + x += direction[0] / sz * self.length + z += direction[2] / sz * self.length + return x, 0, z + + def draw(self): + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT) + + px, _, py = self.player.position + x, _, y = self.pos() + + # Dashed line + glColor3f(.3, .3, .3) + glEnable(GL_LINE_STIPPLE) + glLineStipple(1, 0x7777) + glBegin(GL_LINES) + glVertex2f(px, py) + glVertex2f(x, y) + glEnd() + + # This handle (orientation) + glColor3f(1, 1, 0) + disc(self.radius, x, y) + + glPopAttrib() + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + px, py, pz = self.player.position + hx, hy, hz = self.win.mouse_transform(x, y) + self.set_orientation( + (hx - self.offset[0] - px, + hy - self.offset[1] - py, + hz - self.offset[2] - pz)) + + +class ConeOrientationHandle(OrientationHandle): + tip = 'cone_orientation' + + def get_orientation(self): + return self.player.cone_orientation + + def set_orientation(self, orientation): + self.player.cone_orientation = orientation + + +class ForwardOrientationHandle(OrientationHandle): + tip = 'forward_orientation' + + def get_orientation(self): + return self.player.forward_orientation + + def set_orientation(self, orientation): + self.player.forward_orientation = orientation + + +class ConeAngleHandle(Handle): + radius = .1 + + def pos(self): + px, py, pz = self.player.position + angle = orientation_angle(self.player.cone_orientation) + angle += self.get_angle() * math.pi / 180. / 2 + x = math.cos(angle) * self.length + z = math.sin(angle) * self.length + return px + x, py, pz + z + + def draw(self): + glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT) + + # Fill + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glColor4f(*self.fill_color) + px, _, py = self.player.position + angle = orientation_angle(self.player.cone_orientation) + a = self.get_angle() * math.pi / 180. + disc(self.length, px, py, + start=angle - a/2, + end=angle + a/2) + + # Handle + x, _, y = self.pos() + glColor4f(*self.color) + disc(self.radius, x, y) + glPopAttrib() + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + px, py, pz = self.player.position + hx, hy, hz = self.win.mouse_transform(x, y) + angle = orientation_angle(self.player.cone_orientation) + hangle = orientation_angle((hx - px, hy - py, hz - pz)) + if hangle < angle: + hangle += math.pi * 2 + res = min(max((hangle - angle) * 2, 0), math.pi * 2) + self.set_angle(res * 180. / math.pi) + + +class ConeInnerAngleHandle(ConeAngleHandle): + tip = 'cone_inner_angle' + length = 1. + color = (.2, .8, .2, 1) + fill_color = (0, 1, 0, .1) + + def get_angle(self): + return self.player.cone_inner_angle + + def set_angle(self, angle): + self.player.cone_inner_angle = angle + + +class ConeOuterAngleHandle(ConeAngleHandle): + tip = 'cone_outer_angle' + length = 1.2 + color = (.2, .2, .8, 1) + fill_color = (0, 0, 1, .1) + + def get_angle(self): + return self.player.cone_outer_angle + + def set_angle(self, angle): + self.player.cone_outer_angle = angle + + +class MoreHandle(Handle): + tip = 'More...' + radius = .2 + + open = False + open_width = 1.5 + open_height = 1.5 + + def pos(self): + x, y, z = self.player.position + return x + 1, y, z + 1 + + def draw(self): + x, _, z = self.pos() + + if self.open: + x -= .2 + z += .2 + glPushAttrib(GL_ENABLE_BIT) + glEnable(GL_BLEND) + + glColor4f(1, 1, 1, .8) + glBegin(GL_QUADS) + glVertex2f(x, z) + glVertex2f(x + self.open_width, z) + glVertex2f(x + self.open_width, z - self.open_height) + glVertex2f(x, z - self.open_height) + glEnd() + + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + glColor4f(0, 0, 0, 1) + glBegin(GL_QUADS) + glVertex2f(x, z) + glVertex2f(x + self.open_width, z) + glVertex2f(x + self.open_width, z - self.open_height) + glVertex2f(x, z - self.open_height) + glEnd() + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) + + glPopAttrib() + else: + glColor3f(1, 1, 1) + disc(self.radius, x, z) + + glColor3f(0, 0, 0) + circle(self.radius, x, z) + + r = self.radius - 0.1 + glBegin(GL_LINES) + glVertex2f(x - r, z) + glVertex2f(x + r, z) + glVertex2f(x, z - r) + glVertex2f(x, z + r) + glEnd() + + def begin_drag(self, window, offset): + self.open = True + self.win = window + self.win.set_more_player_handles(self.player) + return self + + def on_mouse_press(self, x, y, button, modifiers): + x, y, z = self.win.mouse_transform(x, y) + for handle in self.win.more_handles: + if handle.hit_test(x, y, z): + return + self.win.set_more_player_handles(None) + self.win.remove_handlers(self) + self.open = False + + def on_mouse_release(self, x, y, button, modifiers): + pass + + +class SliderHandle(Handle): + length = 1. + width = .05 + radius = .1 + + def __init__(self, player, x, z): + super(SliderHandle, self).__init__(player) + self.x = x + self.z = z + + def pos(self): + x, y, z = self.player.position + x += self.x + self.get_value() * self.length + z += self.z + return x, y, z + + def draw(self): + x = self.x + self.player.position[0] + z = self.z + self.player.position[2] + + # Groove + glColor3f(.5, .5, .5) + glBegin(GL_QUADS) + glVertex2f(x, z - self.width/2) + glVertex2f(x + self.length, z - self.width/2) + glVertex2f(x + self.length, z + self.width/2) + glVertex2f(x, z + self.width/2) + glEnd() + + # Thumb + x, _, z = self.pos() + glColor3f(.2, .2, .2) + disc(self.radius, x, z) + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + px, py, pz = self.player.position + hx, hy, hz = self.win.mouse_transform(x, y) + value = float(hx - px - self.x) / self.length + value = min(max(value, 0), 1) + self.set_value(value) + + +class VolumeHandle(SliderHandle): + tip = 'volume' + + def __init__(self, player): + super(VolumeHandle, self).__init__(player, 1, .9) + + def get_value(self): + return self.player.volume + + def set_value(self, value): + self.player.volume = value + + +class ListenerVolumeHandle(SliderHandle): + tip = 'volume' + + def __init__(self, player): + super(ListenerVolumeHandle, self).__init__(player, -.5, -1) + + def get_value(self): + return self.player.volume + + def set_value(self, value): + self.player.volume = value + + +class MinDistanceHandle(SliderHandle): + tip = 'min_distance' + + def __init__(self, player): + super(MinDistanceHandle, self).__init__(player, 1, .6) + + def get_value(self): + return self.player.min_distance / 5. + + def set_value(self, value): + self.player.min_distance = value * 5. + + +class MaxDistanceHandle(SliderHandle): + tip = 'max_distance' + + def __init__(self, player): + super(MaxDistanceHandle, self).__init__(player, 1, .3) + + def get_value(self): + return min(self.player.max_distance / 5., 1.0) + + def set_value(self, value): + self.player.max_distance = value * 5. + + +class ConeOuterGainHandle(SliderHandle): + tip = 'cone_outer_gain' + + def __init__(self, player): + super(ConeOuterGainHandle, self).__init__(player, 1, 0) + + def get_value(self): + return self.player.cone_outer_gain + + def set_value(self, value): + self.player.cone_outer_gain = value + + +class SoundSpaceWindow(pyglet.window.Window): + def __init__(self, **kwargs): + kwargs.update(dict( + caption='Sound Space', + resizable=True, + )) + super(SoundSpaceWindow, self).__init__(**kwargs) + + self.players = [] + self.handles = [] + self.more_handles = [] + + listener = pyglet.media.get_audio_driver().get_listener() + self.handles.append(PositionHandle(listener)) + self.handles.append(ForwardOrientationHandle(listener)) + self.handles.append(ListenerVolumeHandle(listener)) + self.handles.append(LabelHandle(listener)) + + self.tip = pyglet.text.Label('', font_size=10, color=(0, 0, 0, 255), + anchor_y='top', anchor_x='center') + self.tip_player = None + + # pixels per unit + self.zoom = 40 + self.tx = self.width/2 + self.ty = self.height/2 + + def add_player(self, player): + self.players.append(player) + self.handles.append(PositionHandle(player)) + self.handles.append(ConeOrientationHandle(player)) + self.handles.append(ConeInnerAngleHandle(player)) + self.handles.append(ConeOuterAngleHandle(player)) + self.handles.append(LabelHandle(player)) + self.handles.append(MoreHandle(player)) + + def set_more_player_handles(self, player): + if player: + self.more_handles = [ + VolumeHandle(player), + MinDistanceHandle(player), + MaxDistanceHandle(player), + ConeOuterGainHandle(player), + ] + else: + self.more_handles = [] + + def draw_background(self): + glLoadIdentity() + glPushAttrib(GL_CURRENT_BIT) + glColor3f(1, 1, 1) + glBegin(GL_LINES) + for i in range(0, self.width, self.zoom): + glVertex2f(i, 0) + glVertex2f(i, self.height) + for i in range(0, self.height, self.zoom): + glVertex2f(0, i) + glVertex2f(self.width, i) + glEnd() + glPopAttrib() + + def camera_transform(self): + glLoadIdentity() + glTranslatef(self.tx, self.ty, 0) + glScalef(self.zoom, self.zoom, 1) + + def mouse_transform(self, x, y): + return (float(x - self.tx) / self.zoom, + 0, + float(y - self.ty) / self.zoom) + + def player_transform(self, player): + return (player.position[0] * self.zoom + self.tx, + player.position[2] * self.zoom + self.ty) + + def hit_test(self, mouse_x, mouse_y): + x, y, z = self.mouse_transform(mouse_x, mouse_y) + for handle in self.more_handles[::-1] + self.handles[::-1]: + offset = handle.hit_test(x, y, z) + if offset: + return handle, offset + return None, None + + def on_draw(self): + glClearColor(.8, .8, .8, 1) + self.clear() + self.draw_background() + + glPushMatrix() + self.camera_transform() + for handle in self.handles + self.more_handles: + handle.draw() + glPopMatrix() + + if self.tip_player: + player_pos = self.player_transform(self.tip_player) + self.tip.x = player_pos[0] + self.tip.y = player_pos[1] - 15 + self.tip.draw() + + def on_mouse_scroll(self, x, y, dx, dy): + self.zoom += dy * 10 + self.zoom = min(max(self.zoom, 10), 100) + + def on_mouse_press(self, x, y, button, modifiers): + handle, offset = self.hit_test(x, y) + if handle: + self.push_handlers(handle.begin_drag(self, offset)) + else: + self.push_handlers(PanView(self)) + + def on_mouse_motion(self, x, y, dx, dy): + handle, offset = self.hit_test(x, y) + if handle: + self.tip.text = handle.tip + pos = self.player_transform(handle.player) + self.tip_player = handle.player + else: + self.tip.text = '' + + +class PanView: + def __init__(self, window): + self.win = window + + def on_mouse_release(self, x, y, button, modifiers): + self.win.remove_handlers(self) + + def on_mouse_press(self, x, y, button, modifiers): + self.win.remove_handlers(self) + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + self.win.tx += dx + self.win.ty += dy + + +if __name__ == '__main__': + # We swap Y and Z, moving to left-handed system + listener = pyglet.media.get_audio_driver().get_listener() + listener.up_orientation = (0, -1, 0) + + # Start facing up (er, forwards) + listener.forward_orientation = (0, 0, 1) + + listener.label = 'Listener' + + w = SoundSpaceWindow() + r = reader.SpaceReader(w) + r.read(pyglet.resource.file('space.txt')) + player_group = pyglet.media.PlayerGroup(w.players) + player_group.play() + + pyglet.app.run() diff -Nru pyglet-1.4.10/examples/media/synthesizer.py pyglet-1.5.14/examples/media/synthesizer.py --- pyglet-1.4.10/examples/media/synthesizer.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/synthesizer.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,68 @@ +import pyglet + + +class Keyboard: + def __init__(self): + """A VERY basic semi-realtime synthesizer.""" + self.window = pyglet.window.Window(720, 480) + instructions = "Press keys on your keyboard to play notes." + self.instructions = pyglet.text.Label(text=instructions, font_size=20, x=10, y=10) + self.current_note = pyglet.text.Label(text="", font_size=33, x=50, y=200) + + self.c4_notes = {"C": 261.63, "C#": 277.183, + "D": 293.66, "D#": 311.127, + "E": 329.63, + "F": 349.23, "F#": 369.994, + "G": 392.00, "G#": 415.305, + "A": 440.00, "A#": 466.164, + "B": 493.88, "R": 0} + + self.key_map = {pyglet.window.key.S: "C#", + pyglet.window.key.D: "D#", + pyglet.window.key.G: "F#", + pyglet.window.key.H: "G#", + pyglet.window.key.J: "A#", + pyglet.window.key.L: "C#", + pyglet.window.key.SEMICOLON: "D#", + pyglet.window.key.Z: "C", + pyglet.window.key.X: "D", + pyglet.window.key.C: "E", + pyglet.window.key.V: "F", + pyglet.window.key.B: "G", + pyglet.window.key.N: "A", + pyglet.window.key.M: "B", + pyglet.window.key.COMMA: "C", + pyglet.window.key.PERIOD: "D", + pyglet.window.key.BACKSLASH: "E"} + + self.note_cache = {} + + @self.window.event + def on_key_press(key, mod): + try: + self.play_note(self.c4_notes[self.key_map[key]]) + self.current_note.text = "Current note: {0}".format(self.key_map[key]) + except KeyError: + pass + + @self.window.event + def on_draw(): + self.window.clear() + self.instructions.draw() + self.current_note.draw() + + def play_note(self, frequency, length=0.6): + if frequency in self.note_cache: + note_wave = self.note_cache[frequency] + note_wave.play() + else: + adsr = pyglet.media.synthesis.ADSREnvelope(0.05, 0.2, 0.1) + note_wave = pyglet.media.StaticSource( + pyglet.media.synthesis.Sawtooth(duration=length, frequency=frequency, envelope=adsr)) + self.note_cache[frequency] = note_wave + note_wave.play() + + +if __name__ == "__main__": + keyboard = Keyboard() + pyglet.app.run() diff -Nru pyglet-1.4.10/examples/media/video.py pyglet-1.5.14/examples/media/video.py --- pyglet-1.4.10/examples/media/video.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/media/video.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Simple example of video playback. + +Usage:: + + video.py + +See the Programming Guide for a partial list of supported video formats. +""" + +from __future__ import print_function + +__docformat__ = 'restructuredtext' +__version__ = '$Id$' + +import sys +import pyglet + +if len(sys.argv) < 2: + print(__doc__) + sys.exit(1) + +source = pyglet.media.load(sys.argv[1]) +fmt = source.video_format +if not fmt: + print('No video track in this source.') + sys.exit(1) + +player = pyglet.media.Player() +player.queue(source) +player.play() + +window = pyglet.window.Window(width=fmt.width, height=fmt.height) + + +@window.event +def on_draw(): + player.texture.blit(0, 0) + + +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/media_info.py pyglet-1.5.14/examples/media_info.py --- pyglet-1.4.10/examples/media_info.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/media_info.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -#!/usr/bin/env python - -"""Print details of a media file that pyglet can open (requires FFmpeg). - -Usage:: - - media_info.py - -""" - -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import sys -import pyglet - - -def print_ffmpeg_info(): - from pyglet.media import have_ffmpeg - - if have_ffmpeg(): - from pyglet.media.codecs import ffmpeg - print('Using FFmpeg version {0}'.format(ffmpeg.get_version())) - else: - print('FFmpeg not available; required for media decoding.') - print('https://www.ffmpeg.org/download.html\n') - - -def print_source_info(source): - if source.info: - if source.info.title: - print('Title: %s' % source.info.title) - if source.info.album: - print('Album: %s' % source.info.album) - if source.info.author: - print('Author: %s' % source.info.author) - if source.info.year: - print('Year: %d' % source.info.year) - if source.info.track: - print('Track: %d' % source.info.track) - if source.info.genre: - print('Genre: %s' % source.info.genre) - if source.info.copyright: - print('Copyright: %s' % source.info.copyright) - if source.info.comment: - print('Comment: %s' % source.info.comment) - - if source.audio_format: - af = source.audio_format - print('Audio: %d channel(s), %d bits, %.02f Hz' % ( - af.channels, af.sample_size, af.sample_rate)) - - if source.video_format: - vf = source.video_format - if vf.frame_rate: - frame_rate = '%.02f' % vf.frame_rate - else: - frame_rate = 'unknown' - if vf.sample_aspect >= 1: - display_width = vf.sample_aspect * vf.width - display_height = vf.height - else: - display_width = vf.width - display_height = vf.sample_aspect / vf.height - print('Video: %dx%d at aspect %r (displays at %dx%d), %s fps' % ( - vf.width, vf.height, vf.sample_aspect, - display_width, display_height, frame_rate)) - - hours = int(source.duration / 3600) - minutes = int(source.duration / 60) % 60 - seconds = int(source.duration) % 60 - milliseconds = int(source.duration * 1000) % 1000 - print('Duration: %d:%02d:%02d.%03d' % (hours, minutes, seconds, milliseconds)) - - -if __name__ == '__main__': - if len(sys.argv) != 2: - print(__doc__) - print_ffmpeg_info() - sys.exit(1) - - print_ffmpeg_info() - - filename = sys.argv[1] - try: - source = pyglet.media.load(filename, streaming=True) - print_source_info(source) - except pyglet.media.MediaException: - print('Could not open %s' % filename) diff -Nru pyglet-1.4.10/examples/media_player.py pyglet-1.5.14/examples/media_player.py --- pyglet-1.4.10/examples/media_player.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/media_player.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,460 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- -""" -Usage - - media_player.py [options] [ ...] - -Plays the audio / video files listed as arguments, optionally -collecting debug info - -Options - --debug : saves sequence of internal state as a binary *.dbg - --outfile : filename to store the debug info, defaults to filename.dbg - -The raw data captured in the .dbg can be rendered as human readable -using the script report.py -""" - -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import os -import sys -import weakref - -from pyglet.gl import * -import pyglet -from pyglet.window import key - -pyglet.options['debug_media'] = False -# pyglet.options['audio'] = ('openal', 'pulse', 'silent') -from pyglet.media import buffered_logger as bl - - -def draw_rect(x, y, width, height): - glBegin(GL_LINE_LOOP) - glVertex2f(x, y) - glVertex2f(x + width, y) - glVertex2f(x + width, y + height) - glVertex2f(x, y + height) - glEnd() - - -class Control(pyglet.event.EventDispatcher): - x = y = 0 - width = height = 10 - - def __init__(self, parent): - super(Control, self).__init__() - self.parent = weakref.proxy(parent) - - def hit_test(self, x, y): - return (self.x < x < self.x + self.width and - self.y < y < self.y + self.height) - - def capture_events(self): - self.parent.push_handlers(self) - - def release_events(self): - self.parent.remove_handlers(self) - - -class Button(Control): - charged = False - - def draw(self): - if self.charged: - glColor3f(1, 0, 0) - draw_rect(self.x, self.y, self.width, self.height) - glColor3f(1, 1, 1) - self.draw_label() - - def on_mouse_press(self, x, y, button, modifiers): - self.capture_events() - self.charged = True - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - self.charged = self.hit_test(x, y) - - def on_mouse_release(self, x, y, button, modifiers): - self.release_events() - if self.hit_test(x, y): - self.dispatch_event('on_press') - self.charged = False - - -Button.register_event_type('on_press') - - -class TextButton(Button): - def __init__(self, *args, **kwargs): - super(TextButton, self).__init__(*args, **kwargs) - self._text = pyglet.text.Label('', anchor_x='center', anchor_y='center') - - def draw_label(self): - self._text.x = self.x + self.width / 2 - self._text.y = self.y + self.height / 2 - self._text.draw() - - def set_text(self, text): - self._text.text = text - - text = property(lambda self: self._text.text, - set_text) - - -class Slider(Control): - THUMB_WIDTH = 6 - THUMB_HEIGHT = 10 - GROOVE_HEIGHT = 2 - RESPONSIVNESS = 0.3 - - def __init__(self, *args, **kwargs): - super(Slider, self).__init__(*args, **kwargs) - self.seek_value = None - - def draw(self): - center_y = self.y + self.height / 2 - draw_rect(self.x, center_y - self.GROOVE_HEIGHT / 2, - self.width, self.GROOVE_HEIGHT) - pos = self.x + self.value * self.width / (self.max - self.min) - draw_rect(pos - self.THUMB_WIDTH / 2, center_y - self.THUMB_HEIGHT / 2, - self.THUMB_WIDTH, self.THUMB_HEIGHT) - - def coordinate_to_value(self, x): - value = float(x - self.x) / self.width * (self.max - self.min) + self.min - return value - - def on_mouse_press(self, x, y, button, modifiers): - value = self.coordinate_to_value(x) - self.capture_events() - self.dispatch_event('on_begin_scroll') - self.dispatch_event('on_change', value) - pyglet.clock.schedule_once(self.seek_request, self.RESPONSIVNESS) - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - # On some platforms, on_mouse_drag is triggered with a high frequency. - # Seeking takes some time (~200ms). Asking for a seek at every - # on_mouse_drag event would starve the event loop. - # Instead we only record the last mouse position and we - # schedule seek_request to dispatch the on_change event in the future. - # This will allow subsequent on_mouse_drag to change the seek_value - # without triggering yet the on_change event. - value = min(max(self.coordinate_to_value(x), self.min), self.max) - if self.seek_value is None: - # We have processed the last recorded mouse position. - # We re-schedule seek_request - pyglet.clock.schedule_once(self.seek_request, self.RESPONSIVNESS) - self.seek_value = value - - def on_mouse_release(self, x, y, button, modifiers): - self.release_events() - self.dispatch_event('on_end_scroll') - self.seek_value = None - - def seek_request(self, dt): - if self.seek_value is not None: - self.dispatch_event('on_change', self.seek_value) - self.seek_value = None - - -Slider.register_event_type('on_begin_scroll') -Slider.register_event_type('on_end_scroll') -Slider.register_event_type('on_change') - - -class PlayerWindow(pyglet.window.Window): - GUI_WIDTH = 400 - GUI_HEIGHT = 40 - GUI_PADDING = 4 - GUI_BUTTON_HEIGHT = 16 - - def __init__(self, player): - super(PlayerWindow, self).__init__(caption='Media Player', - visible=False, - resizable=True) - # We only keep a weakref to player as we are about to push ourself - # as a handler which would then create a circular reference between - # player and window. - self.player = weakref.proxy(player) - self._player_playing = False - self.player.push_handlers(self) - - self.slider = Slider(self) - self.slider.push_handlers(self) - self.slider.x = self.GUI_PADDING - self.slider.y = self.GUI_PADDING * 2 + self.GUI_BUTTON_HEIGHT - - self.play_pause_button = TextButton(self) - self.play_pause_button.x = self.GUI_PADDING - self.play_pause_button.y = self.GUI_PADDING - self.play_pause_button.height = self.GUI_BUTTON_HEIGHT - self.play_pause_button.width = 45 - self.play_pause_button.on_press = self.on_play_pause - - self.window_button = TextButton(self) - self.window_button.x = self.play_pause_button.x + \ - self.play_pause_button.width + self.GUI_PADDING - self.window_button.y = self.GUI_PADDING - self.window_button.height = self.GUI_BUTTON_HEIGHT - self.window_button.width = 90 - self.window_button.text = 'Windowed' - self.window_button.on_press = lambda: self.set_fullscreen(False) - - self.controls = [ - self.slider, - self.play_pause_button, - self.window_button, - ] - - x = self.window_button.x + self.window_button.width + self.GUI_PADDING - i = 0 - for screen in self.display.get_screens(): - screen_button = TextButton(self) - screen_button.x = x - screen_button.y = self.GUI_PADDING - screen_button.height = self.GUI_BUTTON_HEIGHT - screen_button.width = 80 - screen_button.text = 'Screen %d' % (i + 1) - screen_button.on_press = lambda screen=screen: self.set_fullscreen(True, screen) - self.controls.append(screen_button) - i += 1 - x += screen_button.width + self.GUI_PADDING - - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - - def on_player_next_source(self): - self.gui_update_state() - self.gui_update_source() - self.set_default_video_size() - return True - - def on_player_eos(self): - self.gui_update_state() - pyglet.clock.schedule_once(self.auto_close, 0.1) - return True - - def gui_update_source(self): - if self.player.source: - source = self.player.source - self.slider.min = 0. - self.slider.max = source.duration - self.gui_update_state() - - def gui_update_state(self): - if self.player.playing: - self.play_pause_button.text = 'Pause' - else: - self.play_pause_button.text = 'Play' - - def get_video_size(self): - if not self.player.source or not self.player.source.video_format: - return 0, 0 - video_format = self.player.source.video_format - width = video_format.width - height = video_format.height - if video_format.sample_aspect > 1: - width *= video_format.sample_aspect - elif video_format.sample_aspect < 1: - height /= video_format.sample_aspect - return width, height - - def set_default_video_size(self): - """Make the window size just big enough to show the current - video and the GUI.""" - width = self.GUI_WIDTH - height = self.GUI_HEIGHT - video_width, video_height = self.get_video_size() - width = max(width, video_width) - height += video_height - self.set_size(int(width), int(height)) - - def on_resize(self, width, height): - """Position and size video image.""" - super(PlayerWindow, self).on_resize(width, height) - self.slider.width = width - self.GUI_PADDING * 2 - - height -= self.GUI_HEIGHT - if height <= 0: - return - - video_width, video_height = self.get_video_size() - if video_width == 0 or video_height == 0: - return - display_aspect = width / float(height) - video_aspect = video_width / float(video_height) - if video_aspect > display_aspect: - self.video_width = width - self.video_height = width / video_aspect - else: - self.video_height = height - self.video_width = height * video_aspect - self.video_x = (width - self.video_width) / 2 - self.video_y = (height - self.video_height) / 2 + self.GUI_HEIGHT - - def on_mouse_press(self, x, y, button, modifiers): - for control in self.controls: - if control.hit_test(x, y): - control.on_mouse_press(x, y, button, modifiers) - - def on_key_press(self, symbol, modifiers): - if symbol == key.SPACE: - self.on_play_pause() - elif symbol == key.ESCAPE: - self.dispatch_event('on_close') - elif symbol == key.LEFT: - self.player.seek(0) - elif symbol == key.RIGHT: - self.player.next_source() - - def on_close(self): - self.player.pause() - self.close() - - def auto_close(self, dt): - self.close() - - def on_play_pause(self): - if self.player.playing: - self.player.pause() - else: - if self.player.time >= self.player.source.duration: - self.player.seek(0) - self.player.play() - self.gui_update_state() - - def on_draw(self): - self.clear() - - # Video - if self.player.source and self.player.source.video_format: - video_texture = self.player.texture - video_texture.blit(self.video_x, - self.video_y, - width=self.video_width, - height=self.video_height) - - # GUI - self.slider.value = self.player.time - for control in self.controls: - control.draw() - - def on_begin_scroll(self): - self._player_playing = self.player.playing - self.player.pause() - - def on_change(self, value): - self.player.seek(value) - - def on_end_scroll(self): - if self._player_playing: - self.player.play() - - -def main(target, dbg_file, debug): - set_logging_parameters(target, dbg_file, debug) - - player = pyglet.media.Player() - window = PlayerWindow(player) - - player.queue(pyglet.media.load(filename) for filename in sys.argv[1:]) - - window.gui_update_source() - window.set_visible(True) - window.set_default_video_size() - - # this is an async call - player.play() - window.gui_update_state() - - pyglet.app.run() - - -def set_logging_parameters(target_file, dbg_file, debug): - if not debug: - bl.logger = None - return - if dbg_file is None: - dbg_file = target_file + ".dbg" - else: - dbg_dir = os.path.dirname(dbg_file) - if dbg_dir and not os.path.isdir(dbg_dir): - os.mkdir(dbg_dir) - bl.logger = bl.BufferedLogger(dbg_file) - from pyglet.media.instrumentation import mp_events - # allow to detect crashes by prewriting a crash file, if no crash - # it will be overwrited by the captured data - sample = os.path.basename(target_file) - bl.logger.log("version", mp_events["version"]) - bl.logger.log("crash", sample) - bl.logger.save_log_entries_as_pickle() - bl.logger.clear() - # start the real capture data - bl.logger.log("version", mp_events["version"]) - bl.logger.log("mp.im", sample) - - -def usage(): - print(__doc__) - sys.exit(1) - - -def sysargs_to_mainargs(): - """builds main args from sys.argv""" - if len(sys.argv) < 2: - usage() - debug = False - dbg_file = None - for i in range(2): - if sys.argv[1].startswith("--"): - a = sys.argv.pop(1) - if a.startswith("--debug"): - debug = True - elif a.startswith("--outfile="): - dbg_file = a[len("--outfile="):] - else: - print("Error unknown option:", a) - usage() - target_file = sys.argv[1] - return target_file, dbg_file, debug - - -if __name__ == '__main__': - target_file, dbg_file, debug = sysargs_to_mainargs() - main(target_file, dbg_file, debug) Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/noisy/ball.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/noisy/ball.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/noisy/ball.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/noisy/ball.wav differ diff -Nru pyglet-1.4.10/examples/noisy/noisy.py pyglet-1.5.14/examples/noisy/noisy.py --- pyglet-1.4.10/examples/noisy/noisy.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/noisy/noisy.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""Bounces balls around a window and plays noises. - -This is a simple demonstration of how pyglet efficiently manages many sound -channels without intervention. -""" - -import random -import sys - -from pyglet.gl import * -import pyglet -from pyglet.window import key - -BALL_IMAGE = 'ball.png' -BALL_SOUND = 'ball.wav' - -if len(sys.argv) > 1: - BALL_SOUND = sys.argv[1] - -window = pyglet.window.Window(640, 480) - -sound = pyglet.resource.media(BALL_SOUND, streaming=False) -balls_batch = pyglet.graphics.Batch() -balls = [] -label = pyglet.text.Label('Press space to add a ball, backspace to remove', - font_size=14, - x=window.width // 2, y=10, - anchor_x='center') - - -class Ball(pyglet.sprite.Sprite): - ball_image = pyglet.resource.image(BALL_IMAGE) - width = ball_image.width - height = ball_image.height - - def __init__(self): - x = random.random() * (window.width - self.width) - y = random.random() * (window.height - self.height) - - super(Ball, self).__init__(self.ball_image, x, y, batch=balls_batch) - - self.dx = (random.random() - 0.5) * 1000 - self.dy = (random.random() - 0.5) * 1000 - - def update_position(self, dt): - if self.x <= 0 or self.x + self.width >= window.width: - self.dx *= -1 - sound.play() - if self.y <= 0 or self.y + self.height >= window.height: - self.dy *= -1 - sound.play() - self.x += self.dx * dt - self.y += self.dy * dt - - self.x = min(max(self.x, 0), window.width - self.width) - self.y = min(max(self.y, 0), window.height - self.height) - - -@window.event -def on_key_press(symbol, modifiers): - if symbol == key.SPACE: - balls.append(Ball()) - elif symbol == key.BACKSPACE: - if balls: - del balls[-1] - elif symbol == key.ESCAPE: - window.has_exit = True - - -@window.event -def on_draw(): - window.clear() - balls_batch.draw() - label.draw() - - -def update(dt): - for ball in balls: - ball.update_position(dt) - - -if __name__ == '__main__': - pyglet.clock.schedule_interval(update, 1 / 30.) - pyglet.app.run() diff -Nru pyglet-1.4.10/examples/noisy/README pyglet-1.5.14/examples/noisy/README --- pyglet-1.4.10/examples/noisy/README 2019-07-10 21:01:26.000000000 +0000 +++ pyglet-1.5.14/examples/noisy/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -noisy -===== - -This is an example program that accompanies pyglet (http://www.pyglet.org). -Due to licensing restrictions on some of the assets, this game cannot be used -for commercial purposes. - -The source code is licensed under the BSD license, which is quite permissive -(see the source header for details). - -All artwork and the sound is Copyright 2007 Alex Holkner. diff -Nru pyglet-1.4.10/examples/opengl/opengl_context.py pyglet-1.5.14/examples/opengl/opengl_context.py --- pyglet-1.4.10/examples/opengl/opengl_context.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/opengl/opengl_context.py 2020-12-23 15:47:40.000000000 +0000 @@ -0,0 +1,32 @@ +#!/usr/bin/python +# $Id:$ + +"""In order to use the features of modern OpenGL versions, you must ask for +a specific context version. You can do this by supplying the `major_version` +and `minor_version` attributes for a GL Config. + +This example creates an OpenGL 3 context, prints the version string to stdout, +and exits. +""" + +from __future__ import print_function + +import pyglet + +# Specify the OpenGL version explicitly to request 3.0 features, including +# GLSL 1.3. +# +# Some other attributes relevant to OpenGL 3: +# forward_compatible = True To request a context without deprecated +# functionality +# debug = True To request a debug context +config = pyglet.gl.Config(major_version=3, minor_version=0) + +# Create a context matching the above configuration. Will fail if +# OpenGL 3 is not supported by the driver. +window = pyglet.window.Window(config=config, visible=False) + +# Print the version of the context created. +print('OpenGL version:', window.context.get_info().get_version()) + +window.close() diff -Nru pyglet-1.4.10/examples/opengl/opengl.py pyglet-1.5.14/examples/opengl/opengl.py --- pyglet-1.4.10/examples/opengl/opengl.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/opengl/opengl.py 2020-12-23 15:47:40.000000000 +0000 @@ -0,0 +1,172 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Displays a rotating torus using the pyglet.graphics API. + +This example uses the pyglet.graphics API to render an indexed vertex +list. The vertex list is added to a batch, allowing easy rendered +alongside other vertex lists with minimal overhead. + +This example demonstrates: + + * Setting a 3D projection on a window by overriding the default + on_resize handler + * Enabling multisampling if available + * Drawing simple 3D primitives using the pyglet.graphics API + * Fixed-pipeline lighting +""" + +from math import pi, sin, cos + +import pyglet +from pyglet.gl import * + +try: + # Try and create a window with multisampling (antialiasing) + config = Config(sample_buffers=1, samples=4, depth_size=16, double_buffer=True) + window = pyglet.window.Window(resizable=True, config=config) +except pyglet.window.NoSuchConfigException: + # Fall back to no multisampling for old hardware + window = pyglet.window.Window(resizable=True) + +# Change the window projection to 3D: +window.projection = pyglet.window.Projection3D() + + +@window.event +def on_draw(): + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) + glLoadIdentity() + glTranslatef(0, 0, -4) + glRotatef(rz, 0, 0, 1) + glRotatef(ry, 0, 1, 0) + glRotatef(rx, 1, 0, 0) + batch.draw() + + +def update(dt): + global rx, ry, rz + rx += dt * 1 + ry += dt * 80 + rz += dt * 30 + rx %= 360 + ry %= 360 + rz %= 360 + + +def setup(): + # One-time GL setup + glClearColor(1, 1, 1, 1) + glColor3f(1, 0, 0) + glEnable(GL_DEPTH_TEST) + glEnable(GL_CULL_FACE) + + # Uncomment this line for a wireframe view + #glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) + + # Simple light setup. On Windows GL_LIGHT0 is enabled by default, + # but this is not the case on Linux or Mac, so remember to always + # include it. + glEnable(GL_LIGHTING) + glEnable(GL_LIGHT0) + glEnable(GL_LIGHT1) + + +def create_torus(radius, inner_radius, slices, inner_slices, batch): + + # Create the vertex and normal arrays. + vertices = [] + normals = [] + + u_step = 2 * pi / (slices - 1) + v_step = 2 * pi / (inner_slices - 1) + u = 0. + for i in range(slices): + cos_u = cos(u) + sin_u = sin(u) + v = 0. + for j in range(inner_slices): + cos_v = cos(v) + sin_v = sin(v) + + d = (radius + inner_radius * cos_v) + x = d * cos_u + y = d * sin_u + z = inner_radius * sin_v + + nx = cos_u * cos_v + ny = sin_u * cos_v + nz = sin_v + + vertices.extend([x, y, z]) + normals.extend([nx, ny, nz]) + v += v_step + u += u_step + + # Create a list of triangle indices. + indices = [] + for i in range(slices - 1): + for j in range(inner_slices - 1): + p = i * inner_slices + j + indices.extend([p, p + inner_slices, p + inner_slices + 1]) + indices.extend([p, p + inner_slices + 1, p + 1]) + + # Create a Material and Group for the Model + diffuse = [0.5, 0.0, 0.3, 1.0] + ambient = [0.5, 0.0, 0.3, 1.0] + specular = [1.0, 1.0, 1.0, 1.0] + emission = [0.0, 0.0, 0.0, 1.0] + shininess = 50 + material = pyglet.model.Material("", diffuse, ambient, specular, emission, shininess) + group = pyglet.model.MaterialGroup(material=material) + + vertex_list = batch.add_indexed(len(vertices)//3, + GL_TRIANGLES, + group, + indices, + ('v3f/static', vertices), + ('n3f/static', normals)) + + return pyglet.model.Model([vertex_list], [group], batch) + + +setup() +batch = pyglet.graphics.Batch() +torus_model = create_torus(1, 0.3, 50, 30, batch=batch) +rx = ry = rz = 0 + +pyglet.clock.schedule(update) + +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/opengl/opengl_scissor.py pyglet-1.5.14/examples/opengl/opengl_scissor.py --- pyglet-1.4.10/examples/opengl/opengl_scissor.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/opengl/opengl_scissor.py 2020-12-23 15:47:40.000000000 +0000 @@ -0,0 +1,86 @@ +"""In some cases, you may want to restrict drawing to a specific area +of the Window. To accomplish this, you can use an OpenGL Scissor +area. In this example we define a custom Group that enforces this +Scissor area. Any Sprites/Labels that are assigned to this Group +will not be drawn outside of the area. +""" + +import pyglet +from pyglet.gl import glEnable, glScissor, glDisable, GL_SCISSOR_TEST + + +window = pyglet.window.Window(width=500, height=500) +batch = pyglet.graphics.Batch() + + +@window.event +def on_draw(): + window.clear() + batch.draw() + + +################################################### +# A custom Group class that enforces a Scissor area +################################################### + +class ScissorGroup(pyglet.graphics.Group): + """A Custom Group that defines a "Scissor" area. + + If a Sprite/Label is in this Group, any parts of it that + fall outside of the specified area will not be drawn. + NOTE: You should use the same exact group instance + for every object that will use the group, equal groups + will still be kept seperate. + + :Parameters: + `x` : int + The X coordinate of the Scissor area. + `x` : int + The X coordinate of the Scissor area. + `width` : int + The width of the Scissor area. + `height` : int + The height of the Scissor area. + + """ + + def __init__(self, x, y, width, height, parent=None): + super().__init__(parent) + self.x, self.y = x, y + self.width, self.height = width, height + + @property + def area(self): + return self.x, self.y, self.width, self.height + + @area.setter + def area(self, area): + self.x, self.y, self.width, self.height = area + + def set_state(self): + glEnable(GL_SCISSOR_TEST) + glScissor(self.x, self.y, self.width, self.height) + + def unset_state(self): + glDisable(GL_SCISSOR_TEST) + + +################################################### +# Create an instance of our Group, and some Sprites +################################################### + +# Create an instance of the ScissorGroup that defines the center of the window: +scissor_group = ScissorGroup(x=50, y=50, width=400, height=400) + +# Create a bunch of Sprites assigned to our custom Group. Any parts of these +# Sprites that is outside of the specified area will not be drawn. +sprites = [] +img = pyglet.resource.image('pyglet.png') +for x in range(5): + for y in range(5): + sprite = pyglet.sprite.Sprite( + img, x*img.width, y*img.height, group=scissor_group, batch=batch) + sprites.append(sprite) + + +pyglet.app.run() Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/opengl/pyglet.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/opengl/pyglet.png differ diff -Nru pyglet-1.4.10/examples/opengl_context.py pyglet-1.5.14/examples/opengl_context.py --- pyglet-1.4.10/examples/opengl_context.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/examples/opengl_context.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -#!/usr/bin/python -# $Id:$ - -"""In order to use the features of modern OpenGL versions, you must ask for -a specific context version. You can do this by supplying the `major_version` -and `minor_version` attributes for a GL Config. - -This example creates an OpenGL 3 context, prints the version string to stdout, -and exits. -""" - -from __future__ import print_function - -import pyglet - -# Specify the OpenGL version explicitly to request 3.0 features, including -# GLSL 1.3. -# -# Some other attributes relevant to OpenGL 3: -# forward_compatible = True To request a context without deprecated -# functionality -# debug = True To request a debug context -config = pyglet.gl.Config(major_version=3, minor_version=0) - -# Create a context matching the above configuration. Will fail if -# OpenGL 3 is not supported by the driver. -window = pyglet.window.Window(config=config, visible=False) - -# Print the version of the context created. -print('OpenGL version:', window.context.get_info().get_version()) - -window.close() diff -Nru pyglet-1.4.10/examples/opengl.py pyglet-1.5.14/examples/opengl.py --- pyglet-1.4.10/examples/opengl.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/opengl.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,172 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""Displays a rotating torus using the pyglet.graphics API. - -This example uses the pyglet.graphics API to render an indexed vertex -list. The vertex list is added to a batch, allowing easy rendered -alongside other vertex lists with minimal overhead. - -This example demonstrates: - - * Setting a 3D projection on a window by overriding the default - on_resize handler - * Enabling multisampling if available - * Drawing simple 3D primitives using the pyglet.graphics API - * Fixed-pipeline lighting -""" - -from math import pi, sin, cos - -import pyglet -from pyglet.gl import * - -try: - # Try and create a window with multisampling (antialiasing) - config = Config(sample_buffers=1, samples=4, depth_size=16, double_buffer=True) - window = pyglet.window.Window(resizable=True, config=config) -except pyglet.window.NoSuchConfigException: - # Fall back to no multisampling for old hardware - window = pyglet.window.Window(resizable=True) - -# Change the window projection to 3D: -window.projection = pyglet.window.Projection3D() - - -@window.event -def on_draw(): - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) - glLoadIdentity() - glTranslatef(0, 0, -4) - glRotatef(rz, 0, 0, 1) - glRotatef(ry, 0, 1, 0) - glRotatef(rx, 1, 0, 0) - batch.draw() - - -def update(dt): - global rx, ry, rz - rx += dt * 1 - ry += dt * 80 - rz += dt * 30 - rx %= 360 - ry %= 360 - rz %= 360 - - -def setup(): - # One-time GL setup - glClearColor(1, 1, 1, 1) - glColor3f(1, 0, 0) - glEnable(GL_DEPTH_TEST) - glEnable(GL_CULL_FACE) - - # Uncomment this line for a wireframe view - #glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) - - # Simple light setup. On Windows GL_LIGHT0 is enabled by default, - # but this is not the case on Linux or Mac, so remember to always - # include it. - glEnable(GL_LIGHTING) - glEnable(GL_LIGHT0) - glEnable(GL_LIGHT1) - - -def create_torus(radius, inner_radius, slices, inner_slices, batch): - - # Create the vertex and normal arrays. - vertices = [] - normals = [] - - u_step = 2 * pi / (slices - 1) - v_step = 2 * pi / (inner_slices - 1) - u = 0. - for i in range(slices): - cos_u = cos(u) - sin_u = sin(u) - v = 0. - for j in range(inner_slices): - cos_v = cos(v) - sin_v = sin(v) - - d = (radius + inner_radius * cos_v) - x = d * cos_u - y = d * sin_u - z = inner_radius * sin_v - - nx = cos_u * cos_v - ny = sin_u * cos_v - nz = sin_v - - vertices.extend([x, y, z]) - normals.extend([nx, ny, nz]) - v += v_step - u += u_step - - # Create a list of triangle indices. - indices = [] - for i in range(slices - 1): - for j in range(inner_slices - 1): - p = i * inner_slices + j - indices.extend([p, p + inner_slices, p + inner_slices + 1]) - indices.extend([p, p + inner_slices + 1, p + 1]) - - # Create a Material and Group for the Model - diffuse = [0.5, 0.0, 0.3, 1.0] - ambient = [0.5, 0.0, 0.3, 1.0] - specular = [1.0, 1.0, 1.0, 1.0] - emission = [0.0, 0.0, 0.0, 1.0] - shininess = 50 - material = pyglet.model.Material("", diffuse, ambient, specular, emission, shininess) - group = pyglet.model.MaterialGroup(material=material) - - vertex_list = batch.add_indexed(len(vertices)//3, - GL_TRIANGLES, - group, - indices, - ('v3f/static', vertices), - ('n3f/static', normals)) - - return pyglet.model.Model([vertex_list], [group], batch) - - -setup() -batch = pyglet.graphics.Batch() -torus_model = create_torus(1, 0.3, 50, 30, batch=batch) -rx = ry = rz = 0 - -pyglet.clock.schedule(update) - -pyglet.app.run() Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/pyglet.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/pyglet.png differ diff -Nru pyglet-1.4.10/examples/show_input.py pyglet-1.5.14/examples/show_input.py --- pyglet-1.4.10/examples/show_input.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/show_input.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,402 +0,0 @@ -#!/usr/bin/env python - -"""Graphically show all devices available via the pyglet.input interface. - -Each device is shown in its own collapsed panel. Click on a device panel -to expand it, revealing that device's controls. The controls show the -current live values, and flash white when the value changes. -""" - -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import pyglet -from pyglet import gl - - -class LineGroup(pyglet.graphics.OrderedGroup): - def set_state(self): - gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_LINE) - - def unset_state(self): - gl.glPolygonMode(gl.GL_FRONT_AND_BACK, gl.GL_FILL) - - -class Box(object): - def __init__(self, batch, group=None, - stroke_color=(255, 255, 255, 255), - fill_color=(200, 200, 200, 255)): - self.x1 = 0 - self.y1 = 0 - self.x2 = 0 - self.y2 = 0 - - self.fill_vertices = batch.add(4, gl.GL_QUADS, - pyglet.graphics.OrderedGroup(0, group), - 'v2f', ('c4B', fill_color * 4)) - self.stroke_vertices = batch.add(4, gl.GL_QUADS, - LineGroup(1, group), - 'v2f', ('c4B', stroke_color * 4)) - - def set_bounds(self, x1, y1, x2, y2): - self.x1 = 0 - self.y1 = 0 - self.x2 = 0 - self.y2 = 0 - self.fill_vertices.vertices[:] = (x1, y1, x2, y1, x2, y2, x1, y2) - self.stroke_vertices.vertices[:] = (x1, y1, x2, y1, x2, y2, x1 - 1, y2) - - def set_fill(self, r, g, b): - self.fill_vertices.colors[:] = (r, g, b, 255) * 4 - - def delete(self): - self.fill_vertices.delete() - self.stroke_vertices.delete() - - -class DevicePanel(object): - BORDER_MARGIN = 5 - CONTENT_MARGIN = 8 - - def __init__(self, device): - self.device = device - - self.box = Box(batch, group=background_group, - stroke_color=(0, 0, 200, 255), - fill_color=(200, 200, 255, 255)) - self.name_label = pyglet.text.Label(device.name or 'Unknown device', - font_size=10, - color=(0, 0, 0, 255), - anchor_y='top', - batch=batch, group=text_group) - self.manufacturer_label = pyglet.text.Label(device.manufacturer or '', - font_size=10, - color=(0, 0, 0, 255), anchor_x='right', - anchor_y='top', - batch=batch, group=text_group) - - self.is_open = False - self.widgets = [] - - def set_bounds(self, left, right, top): - self.left = left - self.right = right - self.top = top - self.layout() - - def layout_widgets(self): - max_row_width = self.right - self.left - self.CONTENT_MARGIN * 2 - - row = [] - row_width = 0 - row_height = 0 - - def layout_row(row, x1, y1, x2, y2): - x = x1 - for widget in row: - widget.set_bounds(x, - y1, - x + widget.min_width, - y1 + widget.min_height) - x += widget.min_width - - y = self.bottom + self.CONTENT_MARGIN - for widget in self.widgets: - if widget is None or row_width + widget.min_width > max_row_width: - layout_row(row, - self.left + self.CONTENT_MARGIN, - y - row_height, - self.right - self.CONTENT_MARGIN, - y) - row = [] - y -= row_height - row_width = 0 - - if widget is None: - break - - row.append(widget) - row_width += widget.min_width - row_height = max(row_height, widget.min_height) - - self.bottom = y - self.CONTENT_MARGIN - - def layout(self): - self.title_bottom = self.top - \ - self.name_label.content_height - self.CONTENT_MARGIN * 2 - self.bottom = self.title_bottom - if self.is_open: - self.layout_widgets() - - self.box.set_bounds(self.left + self.BORDER_MARGIN, - self.bottom + self.BORDER_MARGIN, - self.right - self.BORDER_MARGIN, - self.top - self.BORDER_MARGIN) - - self.name_label.x = self.left + self.CONTENT_MARGIN - self.name_label.y = self.top - self.CONTENT_MARGIN - self.manufacturer_label.x = self.right - self.CONTENT_MARGIN - self.manufacturer_label.y = self.top - self.CONTENT_MARGIN - - def hit_test(self, x, y): - return self.left < x < self.right and self.title_bottom < y < self.top - - def toggle(self): - if self.is_open: - self.close() - else: - self.open() - - def open(self): - if self.is_open: - return - - try: - self.device.open() - except pyglet.input.DeviceException as e: - try: - self.device.open(window) - except pyglet.input.DeviceException as e: - print(e) # TODO show error - return - - window.set_mouse_cursor(window.get_system_mouse_cursor('wait')) - for control in self.device.get_controls(): - if isinstance(control, pyglet.input.Button): - widget = ButtonWidget(control, batch, group=text_group) - else: - widget = ControlWidget(control, batch, group=text_group) - self.widgets.append(widget) - - if not self.widgets: - self.widgets.append(NoControlsWidget(batch, group=text_group)) - - self.widgets.append(None) - window.set_mouse_cursor(None) - - self.is_open = True - - def close(self): - if not self.is_open: - return - - for widget in self.widgets: - if widget: - widget.delete() - del self.widgets[:] - - self.device.close() - - self.is_open = False - - -class ControlWidget(object): - BORDER_MARGIN = 2 - CONTENT_MARGIN = 4 - - def __init__(self, control, batch, group=None): - self.control_name = control.name - if not self.control_name: - self.control_name = control.raw_name - self.box = Box(batch, pyglet.graphics.OrderedGroup(0, group)) - self.name_label = pyglet.text.Label(self.control_name, - font_size=10, - anchor_x='left', - anchor_y='bottom', - color=(0, 0, 0, 255), - batch=batch, - group=pyglet.graphics.OrderedGroup(1, group)) - self.value_label = pyglet.text.Label(' ', - font_size=8, - anchor_x='right', - anchor_y='bottom', - color=(0, 0, 0, 255), - batch=batch, - group=pyglet.graphics.OrderedGroup(1, group)) - - self.min_width = \ - self.name_label.content_width + \ - self.value_label.content_width + self.CONTENT_MARGIN * 2 - self.min_height = self.name_label.content_height + self.CONTENT_MARGIN * 2 - - self.relative = isinstance(control, pyglet.input.RelativeAxis) - self.fade = 200 - - self.control = control - control.push_handlers(self) - - def set_bounds(self, x1, y1, x2, y2): - self.box.set_bounds( - x1 + self.BORDER_MARGIN, - y1 + self.BORDER_MARGIN, - x2 - self.BORDER_MARGIN, - y2 - self.BORDER_MARGIN) - self.name_label.x = x1 + self.CONTENT_MARGIN - self.name_label.y = y1 + self.CONTENT_MARGIN - self.value_label.x = x2 - self.CONTENT_MARGIN - self.value_label.y = y1 + self.CONTENT_MARGIN - - def delete(self): - if self in changed_widgets: - changed_widgets.remove(self) - self.control.remove_handlers(self) - self.name_label.delete() - self.value_label.delete() - self.box.delete() - - def on_change(self, value): - self.value = value - self.fade = 255 - changed_widgets.add(self) - - def update(self): - self.value_label.text = str(self.value) - if self.relative and self.value: - self.value = 0 - changed_widgets.add(self) - - self.box.set_fill(self.fade, self.fade, self.fade) - if self.fade > 200: - self.fade = max(200, self.fade - 10) - changed_widgets.add(self) - - -class ButtonWidget(ControlWidget): - BORDER_MARGIN = 2 - CONTENT_MARGIN = 4 - - def __init__(self, control, batch, group=None): - self.control_name = control.name - if not self.control_name: - self.control_name = control.raw_name - self.box = Box(batch, pyglet.graphics.OrderedGroup(0, group)) - self.name_label = pyglet.text.Label(self.control_name, - font_size=10, - anchor_x='center', - anchor_y='bottom', - color=(0, 0, 0, 255), - batch=batch, - group=pyglet.graphics.OrderedGroup(1, group)) - - self.min_width = self.name_label.content_width + self.CONTENT_MARGIN * 2 - self.min_height = self.name_label.content_height + self.CONTENT_MARGIN * 2 - - self.fade = 200 - - self.control = control - control.push_handlers(self) - - def set_bounds(self, x1, y1, x2, y2): - self.box.set_bounds( - x1 + self.BORDER_MARGIN, - y1 + self.BORDER_MARGIN, - x2 - self.BORDER_MARGIN, - y2 - self.BORDER_MARGIN) - self.name_label.x = (x1 + x2) // 2 - self.name_label.y = y1 + self.CONTENT_MARGIN - - def delete(self): - if self in changed_widgets: - changed_widgets.remove(self) - self.control.remove_handlers(self) - self.name_label.delete() - self.box.delete() - - def on_change(self, value): - self.value = value - if value: - self.fade = 255 - changed_widgets.add(self) - - def update(self): - self.box.set_fill(self.fade, self.fade, self.fade) - if not self.value and self.fade > 200: - self.fade = max(200, self.fade - 10) - changed_widgets.add(self) - - -class NoControlsWidget(object): - CONTENT_MARGIN = 4 - - def __init__(self, batch, group): - self.label = pyglet.text.Label('No controls on this device.', - font_size=10, - color=(0, 0, 0, 255), - anchor_y='bottom', - batch=batch, - group=group) - - self.min_width = self.label.content_width + self.CONTENT_MARGIN * 2 - self.min_height = self.label.content_height + self.CONTENT_MARGIN * 2 - - def set_bounds(self, x1, y1, x2, y2): - self.label.x = x1 + ControlWidget.CONTENT_MARGIN - self.label.y = y1 + ControlWidget.CONTENT_MARGIN - - def delete(self): - self.label.delete() - - -window = pyglet.window.Window(caption='Input Devices', resizable=True) -batch = pyglet.graphics.Batch() -background_group = pyglet.graphics.OrderedGroup(0) -text_group = pyglet.graphics.OrderedGroup(1) - -panels = [DevicePanel(device) for device in pyglet.input.get_devices()] -help_label = pyglet.text.Label( - 'Click on a device name to show or hide its controls.', - x=DevicePanel.CONTENT_MARGIN, - anchor_y='top', - font_size=10, - color=(255, 255, 255, 255), - batch=batch, - group=background_group) - - -def layout_panels(): - y = window.height - for panel in panels: - panel.set_bounds(left=0, right=window.width, top=y) - y = panel.bottom - help_label.y = y - - -@window.event -def on_draw(): - gl.glClearColor(0.3, 0.3, 0.4, 1.0) - window.clear() - batch.draw() - window.invalid = False - - -@window.event -def on_resize(width, height): - layout_panels() - window.invalid = True - return pyglet.event.EVENT_UNHANDLED - - -@window.event -def on_mouse_press(x, y, button, modifiers): - for panel in panels: - if panel.hit_test(x, y): - panel.toggle() - layout_panels() - window.invalid = True - - -changed_widgets = set() - - -def update(dt): - pending = list(changed_widgets) - changed_widgets.clear() - for widget in pending: - widget.update() - window.invalid = True - - -pyglet.clock.schedule_interval(update, 0.05) -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/soundspace/reader.py pyglet-1.5.14/examples/soundspace/reader.py --- pyglet-1.4.10/examples/soundspace/reader.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/soundspace/reader.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- -# $Id$ - -import os - -from pyglet import media - - -class ReaderException(Exception): - pass - - -class PlayerReader(object): - def __init__(self, player): - self.player = player - - def line(self, line, lineno): - parts = line.split() - if parts[0] == 'position': - if len(parts) < 4: - raise ReaderException('Invalid position line %d' % lineno) - self.player.position = tuple([float(x) for x in parts[1:]]) - if parts[0] == 'cone_orientation': - if len(parts) < 4: - raise ReaderException('Invalid orientation line %d' % lineno) - self.player.cone_orientation = tuple([float(x) for x in parts[1:]]) - elif parts[0] == 'outer_cone_angle': - if len(parts) < 2: - raise ReaderException('Invalid angle line %d' % lineno) - self.player.cone_outer_angle = float(parts[1]) - elif parts[0] == 'inner_cone_angle': - if len(parts) < 2: - raise ReaderException('Invalid angle line %d' % lineno) - self.player.cone_inner_angle = float(parts[1]) - elif parts[0] == 'label': - if len(parts) < 2: - raise ReaderException('Invalid label line %d' % lineno) - self.player.label = parts[1] - - -class SpaceReader(object): - def __init__(self, space): - self.basedir = '' - self.space = space - - def read(self, file): - if not hasattr(file, 'read'): - self.basedir = os.path.dirname(file) - file = open(file, 'rt') - elif hasattr(file, 'name'): - self.basedir = os.path.dirname(file.name) - reader = None - lineno = 0 - for line in file: - lineno += 1 - - if not isinstance('', bytes) and isinstance(line, bytes): - # decode bytes to str on Python 3 - line = line.decode('ascii') - - if not line.strip() or line.startswith('#'): - continue - if line.startswith(' '): - if not reader: - raise ReaderException('Unexpected indented block line %d' % lineno) - reader.line(line, lineno) - else: - reader = None - parts = line.split() - if parts[0] == 'loop': - if len(parts) < 2: - raise ReaderException('No loop filename line %d' % lineno) - player = media.Player() - player.loop = True - player.queue(self.source(parts[1], streaming=False)) - self.space.add_player(player) - reader = PlayerReader(player) - - def source(self, filename, **kwargs): - filename = os.path.join(self.basedir, filename) - return media.load(filename, **kwargs) diff -Nru pyglet-1.4.10/examples/soundspace/README pyglet-1.5.14/examples/soundspace/README --- pyglet-1.4.10/examples/soundspace/README 2019-07-10 21:01:26.000000000 +0000 +++ pyglet-1.5.14/examples/soundspace/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -Sound Space -=========== - -This is a toy program for playing with positional audio in pyglet -(http://www.pyglet.org). On Linux, OpenAL is required. - -The source code is licensed under the BSD license, which is quite permissive -(see the source headers for details). - -Audio samples were generated with Apple GarageBand. - -Usage ------ - -Run the toy with:: - - python soundspace.py - -Everything is controlled with the mouse. Hover over a control to -see its name. Click and drag an empty area to pan the view, and scroll the -scroll wheel to zoom in and out. - -The red triangles can be dragged to move the position of a player or listener. - -The dashed line to the yellow handle shows the cone orientation; this can be -reoriented by dragging it. - -The blue and green segments represent the outer and inner cone angles, -respectively. You can resize the angle by dragging the handle attached to the -cone. - -There is a master volume control beneath the listener. - -Click the (+) sign on a player to show the min_gain, max_gain, cone_outer_gain -and volume controls. - -The initial configuration is given in res/space.txt; it should be -self-explanatory (parsed by reader.py). Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/soundspace/res/bass.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/soundspace/res/bass.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/soundspace/res/drums.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/soundspace/res/drums.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/soundspace/res/guitar.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/soundspace/res/guitar.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/soundspace/res/piano.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/soundspace/res/piano.wav differ diff -Nru pyglet-1.4.10/examples/soundspace/res/space.txt pyglet-1.5.14/examples/soundspace/res/space.txt --- pyglet-1.4.10/examples/soundspace/res/space.txt 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/soundspace/res/space.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -loop drums.wav - label Drums - position -1.5 0 4 - cone_orientation 1 0 -1 - outer_cone_angle 360 - inner_cone_angle 90 -loop piano.wav - label Piano - position 3 0 4.5 - cone_orientation -.5 0 -1 - outer_cone_angle 360 - inner_cone_angle 90 -loop bass.wav - label Bass - position -3 0 -4 - cone_orientation .5 0 1 - outer_cone_angle 270 - inner_cone_angle 90 -loop guitar.wav - label Guitar - position 5 0 -3 - cone_orientation -1.5 0 1 - outer_cone_angle 270 - inner_cone_angle 90 diff -Nru pyglet-1.4.10/examples/soundspace/soundspace.py pyglet-1.5.14/examples/soundspace/soundspace.py --- pyglet-1.4.10/examples/soundspace/soundspace.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/soundspace/soundspace.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,623 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- -# $Id$ - -import math - -import pyglet -from pyglet.gl import * - -import reader - -pyglet.resource.path.append('res') -pyglet.resource.reindex() - -# Default to OpenAL if available: -pyglet.options['audio'] = 'openal', 'pulse', 'directsound', 'silent' - - -def disc(r, x, y, slices=20, start=0, end=2*math.pi): - d = (end - start) / (slices - 1) - s = start - points = [(x, y)] + [(x + r * math.cos(a*d+s), y + r * math.sin(a*d+s)) - for a in range(slices)] - points = ((GLfloat * 2) * len(points))(*points) - glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) - glEnableClientState(GL_VERTEX_ARRAY) - glVertexPointer(2, GL_FLOAT, 0, points) - glDrawArrays(GL_TRIANGLE_FAN, 0, len(points)) - glPopClientAttrib() - - -def circle(r, x, y, slices=20): - d = 2 * math.pi / slices - points = [(x + r * math.cos(a*d), y + r * math.sin(a*d)) for a in range(slices)] - points = ((GLfloat * 2) * len(points))(*points) - glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) - glEnableClientState(GL_VERTEX_ARRAY) - glVertexPointer(2, GL_FLOAT, 0, points) - glDrawArrays(GL_LINE_LOOP, 0, len(points)) - glPopClientAttrib() - - -def orientation_angle(orientation): - return math.atan2(orientation[2], orientation[0]) - - -class Handle(object): - tip = '' - - def __init__(self, player): - self.player = player - - def hit_test(self, x, y, z): - dx, dy, dz = [a - b for a, b in zip(self.pos(), (x, y, z))] - if dx * dx + dy * dy + dz * dz < self.radius * self.radius: - return -dx, -dy, -dz - - def draw(self): - pass - - def begin_drag(self, window, offset): - self.win = window - self.offset = offset - return self - - def on_mouse_press(self, x, y, button, modifiers): - self.win.remove_handlers(self) - - def on_mouse_release(self, x, y, button, modifiers): - self.win.remove_handlers(self) - - -class LabelHandle(Handle): - def __init__(self, player): - super(LabelHandle, self).__init__(player) - self.text = pyglet.text.Label('', font_size=10, color=(0, 0, 0, 255), - anchor_y='top', anchor_x='center') - - def hit_test(self, x, y, z): - return None - - def draw(self): - if hasattr(self.player, 'label'): - x, _, y = self.player.position - - # ech. fudge scale back to 1 - mat = (GLfloat * 16)() - glGetFloatv(GL_MODELVIEW_MATRIX, mat) - - glPushMatrix() - glTranslatef(x, y, 0) - glScalef(1/mat[0], 1/mat[5], 1/mat[10]) - glTranslatef(0, -5, 0) - - self.text.text = self.player.label - self.text.draw() - - glPopMatrix() - - -class PositionHandle(Handle): - tip = 'position' - radius = .3 - - def draw(self): - glPushMatrix() - glTranslatef(self.player.position[0], self.player.position[2], 0) - glColor3f(1, 0, 0) - glBegin(GL_TRIANGLES) - glVertex2f(0, self.radius) - glVertex2f(-self.radius * math.sqrt(3) / 2, -.5 * self.radius) - glVertex2f(self.radius * math.sqrt(3) / 2, -.5 * self.radius) - glEnd() - glPopMatrix() - - def pos(self): - return self.player.position - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - pos = self.win.mouse_transform(x, y) - self.player.position = \ - (pos[0] - self.offset[0], - pos[1] - self.offset[1], - pos[2] - self.offset[2]) - - -class OrientationHandle(Handle): - radius = .1 - length = 1.5 - - def pos(self): - x, _, z = self.player.position - direction = self.get_orientation() - sz = math.sqrt(direction[0] ** 2 + direction[1] ** 2 + direction[2] ** 2) or 1 - if sz != 0: - x += direction[0] / sz * self.length - z += direction[2] / sz * self.length - return x, 0, z - - def draw(self): - glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT) - - px, _, py = self.player.position - x, _, y = self.pos() - - # Dashed line - glColor3f(.3, .3, .3) - glEnable(GL_LINE_STIPPLE) - glLineStipple(1, 0x7777) - glBegin(GL_LINES) - glVertex2f(px, py) - glVertex2f(x, y) - glEnd() - - # This handle (orientation) - glColor3f(1, 1, 0) - disc(self.radius, x, y) - - glPopAttrib() - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - px, py, pz = self.player.position - hx, hy, hz = self.win.mouse_transform(x, y) - self.set_orientation( - (hx - self.offset[0] - px, - hy - self.offset[1] - py, - hz - self.offset[2] - pz)) - - -class ConeOrientationHandle(OrientationHandle): - tip = 'cone_orientation' - - def get_orientation(self): - return self.player.cone_orientation - - def set_orientation(self, orientation): - self.player.cone_orientation = orientation - - -class ForwardOrientationHandle(OrientationHandle): - tip = 'forward_orientation' - - def get_orientation(self): - return self.player.forward_orientation - - def set_orientation(self, orientation): - self.player.forward_orientation = orientation - - -class ConeAngleHandle(Handle): - radius = .1 - - def pos(self): - px, py, pz = self.player.position - angle = orientation_angle(self.player.cone_orientation) - angle += self.get_angle() * math.pi / 180. / 2 - x = math.cos(angle) * self.length - z = math.sin(angle) * self.length - return px + x, py, pz + z - - def draw(self): - glPushAttrib(GL_ENABLE_BIT | GL_CURRENT_BIT) - - # Fill - glEnable(GL_BLEND) - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) - glColor4f(*self.fill_color) - px, _, py = self.player.position - angle = orientation_angle(self.player.cone_orientation) - a = self.get_angle() * math.pi / 180. - disc(self.length, px, py, - start=angle - a/2, - end=angle + a/2) - - # Handle - x, _, y = self.pos() - glColor4f(*self.color) - disc(self.radius, x, y) - glPopAttrib() - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - px, py, pz = self.player.position - hx, hy, hz = self.win.mouse_transform(x, y) - angle = orientation_angle(self.player.cone_orientation) - hangle = orientation_angle((hx - px, hy - py, hz - pz)) - if hangle < angle: - hangle += math.pi * 2 - res = min(max((hangle - angle) * 2, 0), math.pi * 2) - self.set_angle(res * 180. / math.pi) - - -class ConeInnerAngleHandle(ConeAngleHandle): - tip = 'cone_inner_angle' - length = 1. - color = (.2, .8, .2, 1) - fill_color = (0, 1, 0, .1) - - def get_angle(self): - return self.player.cone_inner_angle - - def set_angle(self, angle): - self.player.cone_inner_angle = angle - - -class ConeOuterAngleHandle(ConeAngleHandle): - tip = 'cone_outer_angle' - length = 1.2 - color = (.2, .2, .8, 1) - fill_color = (0, 0, 1, .1) - - def get_angle(self): - return self.player.cone_outer_angle - - def set_angle(self, angle): - self.player.cone_outer_angle = angle - - -class MoreHandle(Handle): - tip = 'More...' - radius = .2 - - open = False - open_width = 1.5 - open_height = 1.5 - - def pos(self): - x, y, z = self.player.position - return x + 1, y, z + 1 - - def draw(self): - x, _, z = self.pos() - - if self.open: - x -= .2 - z += .2 - glPushAttrib(GL_ENABLE_BIT) - glEnable(GL_BLEND) - - glColor4f(1, 1, 1, .8) - glBegin(GL_QUADS) - glVertex2f(x, z) - glVertex2f(x + self.open_width, z) - glVertex2f(x + self.open_width, z - self.open_height) - glVertex2f(x, z - self.open_height) - glEnd() - - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) - glColor4f(0, 0, 0, 1) - glBegin(GL_QUADS) - glVertex2f(x, z) - glVertex2f(x + self.open_width, z) - glVertex2f(x + self.open_width, z - self.open_height) - glVertex2f(x, z - self.open_height) - glEnd() - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) - - glPopAttrib() - else: - glColor3f(1, 1, 1) - disc(self.radius, x, z) - - glColor3f(0, 0, 0) - circle(self.radius, x, z) - - r = self.radius - 0.1 - glBegin(GL_LINES) - glVertex2f(x - r, z) - glVertex2f(x + r, z) - glVertex2f(x, z - r) - glVertex2f(x, z + r) - glEnd() - - def begin_drag(self, window, offset): - self.open = True - self.win = window - self.win.set_more_player_handles(self.player) - return self - - def on_mouse_press(self, x, y, button, modifiers): - x, y, z = self.win.mouse_transform(x, y) - for handle in self.win.more_handles: - if handle.hit_test(x, y, z): - return - self.win.set_more_player_handles(None) - self.win.remove_handlers(self) - self.open = False - - def on_mouse_release(self, x, y, button, modifiers): - pass - - -class SliderHandle(Handle): - length = 1. - width = .05 - radius = .1 - - def __init__(self, player, x, z): - super(SliderHandle, self).__init__(player) - self.x = x - self.z = z - - def pos(self): - x, y, z = self.player.position - x += self.x + self.get_value() * self.length - z += self.z - return x, y, z - - def draw(self): - x = self.x + self.player.position[0] - z = self.z + self.player.position[2] - - # Groove - glColor3f(.5, .5, .5) - glBegin(GL_QUADS) - glVertex2f(x, z - self.width/2) - glVertex2f(x + self.length, z - self.width/2) - glVertex2f(x + self.length, z + self.width/2) - glVertex2f(x, z + self.width/2) - glEnd() - - # Thumb - x, _, z = self.pos() - glColor3f(.2, .2, .2) - disc(self.radius, x, z) - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - px, py, pz = self.player.position - hx, hy, hz = self.win.mouse_transform(x, y) - value = float(hx - px - self.x) / self.length - value = min(max(value, 0), 1) - self.set_value(value) - - -class VolumeHandle(SliderHandle): - tip = 'volume' - - def __init__(self, player): - super(VolumeHandle, self).__init__(player, 1, .9) - - def get_value(self): - return self.player.volume - - def set_value(self, value): - self.player.volume = value - - -class ListenerVolumeHandle(SliderHandle): - tip = 'volume' - - def __init__(self, player): - super(ListenerVolumeHandle, self).__init__(player, -.5, -1) - - def get_value(self): - return self.player.volume - - def set_value(self, value): - self.player.volume = value - - -class MinDistanceHandle(SliderHandle): - tip = 'min_distance' - - def __init__(self, player): - super(MinDistanceHandle, self).__init__(player, 1, .6) - - def get_value(self): - return self.player.min_distance / 5. - - def set_value(self, value): - self.player.min_distance = value * 5. - - -class MaxDistanceHandle(SliderHandle): - tip = 'max_distance' - - def __init__(self, player): - super(MaxDistanceHandle, self).__init__(player, 1, .3) - - def get_value(self): - return min(self.player.max_distance / 5., 1.0) - - def set_value(self, value): - self.player.max_distance = value * 5. - - -class ConeOuterGainHandle(SliderHandle): - tip = 'cone_outer_gain' - - def __init__(self, player): - super(ConeOuterGainHandle, self).__init__(player, 1, 0) - - def get_value(self): - return self.player.cone_outer_gain - - def set_value(self, value): - self.player.cone_outer_gain = value - - -class SoundSpaceWindow(pyglet.window.Window): - def __init__(self, **kwargs): - kwargs.update(dict( - caption='Sound Space', - resizable=True, - )) - super(SoundSpaceWindow, self).__init__(**kwargs) - - self.players = [] - self.handles = [] - self.more_handles = [] - - listener = pyglet.media.get_audio_driver().get_listener() - self.handles.append(PositionHandle(listener)) - self.handles.append(ForwardOrientationHandle(listener)) - self.handles.append(ListenerVolumeHandle(listener)) - self.handles.append(LabelHandle(listener)) - - self.tip = pyglet.text.Label('', font_size=10, color=(0, 0, 0, 255), - anchor_y='top', anchor_x='center') - self.tip_player = None - - # pixels per unit - self.zoom = 40 - self.tx = self.width/2 - self.ty = self.height/2 - - def add_player(self, player): - self.players.append(player) - self.handles.append(PositionHandle(player)) - self.handles.append(ConeOrientationHandle(player)) - self.handles.append(ConeInnerAngleHandle(player)) - self.handles.append(ConeOuterAngleHandle(player)) - self.handles.append(LabelHandle(player)) - self.handles.append(MoreHandle(player)) - - def set_more_player_handles(self, player): - if player: - self.more_handles = [ - VolumeHandle(player), - MinDistanceHandle(player), - MaxDistanceHandle(player), - ConeOuterGainHandle(player), - ] - else: - self.more_handles = [] - - def draw_background(self): - glLoadIdentity() - glPushAttrib(GL_CURRENT_BIT) - glColor3f(1, 1, 1) - glBegin(GL_LINES) - for i in range(0, self.width, self.zoom): - glVertex2f(i, 0) - glVertex2f(i, self.height) - for i in range(0, self.height, self.zoom): - glVertex2f(0, i) - glVertex2f(self.width, i) - glEnd() - glPopAttrib() - - def camera_transform(self): - glLoadIdentity() - glTranslatef(self.tx, self.ty, 0) - glScalef(self.zoom, self.zoom, 1) - - def mouse_transform(self, x, y): - return (float(x - self.tx) / self.zoom, - 0, - float(y - self.ty) / self.zoom) - - def player_transform(self, player): - return (player.position[0] * self.zoom + self.tx, - player.position[2] * self.zoom + self.ty) - - def hit_test(self, mouse_x, mouse_y): - x, y, z = self.mouse_transform(mouse_x, mouse_y) - for handle in self.more_handles[::-1] + self.handles[::-1]: - offset = handle.hit_test(x, y, z) - if offset: - return handle, offset - return None, None - - def on_draw(self): - glClearColor(.8, .8, .8, 1) - self.clear() - self.draw_background() - - glPushMatrix() - self.camera_transform() - for handle in self.handles + self.more_handles: - handle.draw() - glPopMatrix() - - if self.tip_player: - player_pos = self.player_transform(self.tip_player) - self.tip.x = player_pos[0] - self.tip.y = player_pos[1] - 15 - self.tip.draw() - - def on_mouse_scroll(self, x, y, dx, dy): - self.zoom += dy * 10 - self.zoom = min(max(self.zoom, 10), 100) - - def on_mouse_press(self, x, y, button, modifiers): - handle, offset = self.hit_test(x, y) - if handle: - self.push_handlers(handle.begin_drag(self, offset)) - else: - self.push_handlers(PanView(self)) - - def on_mouse_motion(self, x, y, dx, dy): - handle, offset = self.hit_test(x, y) - if handle: - self.tip.text = handle.tip - pos = self.player_transform(handle.player) - self.tip_player = handle.player - else: - self.tip.text = '' - - -class PanView(object): - def __init__(self, window): - self.win = window - - def on_mouse_release(self, x, y, button, modifiers): - self.win.remove_handlers(self) - - def on_mouse_press(self, x, y, button, modifiers): - self.win.remove_handlers(self) - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - self.win.tx += dx - self.win.ty += dy - - -if __name__ == '__main__': - # We swap Y and Z, moving to left-handed system - listener = pyglet.media.get_audio_driver().get_listener() - listener.up_orientation = (0, -1, 0) - - # Start facing up (er, forwards) - listener.forward_orientation = (0, 0, 1) - - listener.label = 'Listener' - - w = SoundSpaceWindow() - r = reader.SpaceReader(w) - r.read(pyglet.resource.file('space.txt')) - player_group = pyglet.media.PlayerGroup(w.players) - player_group.play() - - pyglet.app.run() diff -Nru pyglet-1.4.10/examples/synthesizer.py pyglet-1.5.14/examples/synthesizer.py --- pyglet-1.4.10/examples/synthesizer.py 2020-01-08 23:56:30.000000000 +0000 +++ pyglet-1.5.14/examples/synthesizer.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -import pyglet - - -class Keyboard: - def __init__(self): - """A VERY basic semi-realtime synthesizer.""" - self.window = pyglet.window.Window(720, 480) - instructions = "Press keys on your keyboard to play notes." - self.instructions = pyglet.text.Label(text=instructions, font_size=20, x=10, y=10) - self.current_note = pyglet.text.Label(text="", font_size=33, x=50, y=200) - - self.c4_notes = {"C": 261.63, "C#": 277.183, - "D": 293.66, "D#": 311.127, - "E": 329.63, - "F": 349.23, "F#": 369.994, - "G": 392.00, "G#": 415.305, - "A": 440.00, "A#": 466.164, - "B": 493.88, "R": 0} - - self.key_map = {pyglet.window.key.S: "C#", - pyglet.window.key.D: "D#", - pyglet.window.key.G: "F#", - pyglet.window.key.H: "G#", - pyglet.window.key.J: "A#", - pyglet.window.key.L: "C#", - pyglet.window.key.SEMICOLON: "D#", - pyglet.window.key.Z: "C", - pyglet.window.key.X: "D", - pyglet.window.key.C: "E", - pyglet.window.key.V: "F", - pyglet.window.key.B: "G", - pyglet.window.key.N: "A", - pyglet.window.key.M: "B", - pyglet.window.key.COMMA: "C", - pyglet.window.key.PERIOD: "D", - pyglet.window.key.BACKSLASH: "E"} - - self.note_cache = {} - - @self.window.event - def on_key_press(key, mod): - try: - self.play_note(self.c4_notes[self.key_map[key]]) - self.current_note.text = "Current note: {0}".format(self.key_map[key]) - except KeyError: - pass - - @self.window.event - def on_draw(): - self.window.clear() - self.instructions.draw() - self.current_note.draw() - - def play_note(self, frequency, length=0.6): - if frequency in self.note_cache: - note_wave = self.note_cache[frequency] - note_wave.play() - else: - adsr = pyglet.media.synthesis.ADSREnvelope(0.05, 0.2, 0.1) - note_wave = pyglet.media.StaticSource( - pyglet.media.synthesis.Sawtooth(duration=length, frequency=frequency, envelope=adsr)) - self.note_cache[frequency] = note_wave - note_wave.play() - - -if __name__ == "__main__": - keyboard = Keyboard() - pyglet.app.run() diff -Nru pyglet-1.4.10/examples/tablet.py pyglet-1.5.14/examples/tablet.py --- pyglet-1.4.10/examples/tablet.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/tablet.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -#!/usr/bin/python -# $Id:$ - -from __future__ import print_function - -import pyglet - -window = pyglet.window.Window() -tablets = pyglet.input.get_tablets() -canvases = [] - -if tablets: - print('Tablets:') - for i, tablet in enumerate(tablets): - print(' (%d) %s' % (i + 1, tablet.name)) - print('Press number key to open corresponding tablet device.') -else: - print('No tablets found.') - - -@window.event -def on_text(text): - try: - index = int(text) - 1 - except ValueError: - return - - if not (0 <= index < len(tablets)): - return - - name = tablets[i].name - - try: - canvas = tablets[i].open(window) - except pyglet.input.DeviceException: - print('Failed to open tablet %d on window' % index) - - print('Opened %s' % name) - - @canvas.event - def on_enter(cursor): - print('%s: on_enter(%r)' % (name, cursor)) - - @canvas.event - def on_leave(cursor): - print('%s: on_leave(%r)' % (name, cursor)) - - @canvas.event - def on_motion(cursor, x, y, pressure): - print('%s: on_motion(%r, %r, %r, %r)' % (name, cursor, x, y, pressure)) - - -@window.event -def on_mouse_press(x, y, button, modifiers): - print('on_mouse_press(%r, %r, %r, %r' % (x, y, button, modifiers)) - - -@window.event -def on_mouse_release(x, y, button, modifiers): - print('on_mouse_release(%r, %r, %r, %r' % (x, y, button, modifiers)) - - -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/text/font_comparison.py pyglet-1.5.14/examples/text/font_comparison.py --- pyglet-1.4.10/examples/text/font_comparison.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/text/font_comparison.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,157 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""A simple tool that may be used to explore font faces. (Windows only) + +Only the fonts installed in the system are visible. + +Use the left/right cursor keys to change font faces. + +By default only the pyglet safe fonts are shown, toggle the safe flag +to see all. + +Don't include tabs in the text sample (see +http://pyglet.org/doc-current/programming_guide/text.html#id9 ) +""" + +from __future__ import print_function, unicode_literals + +import pyglet +import pyglet.font.win32query as wq + + +# support to generate a sample text good to spot monospace compliance. +# Chosen to do a table of fields_per_line columns, each column with field_size +# characters. Fields are filled with a rolling subset of ASCII characters. +class SampleTable: + field_size = 7 + gap_size = 3 + fields_per_line = 7 + spaces = ' ' * field_size + max_chars_per_line = (field_size + gap_size) * fields_per_line - gap_size + + def __init__(self): + self.lines = [] + self.current_line = '' + + def newline(self): + self.lines.append(self.current_line) + self.current_line = '' + + def add_field(self, s): + assert len(s) <= self.field_size + to_add = self.spaces[len(s):] + s + if self.current_line: + to_add = ' ' * self.gap_size + to_add + if len(self.current_line) + len(to_add) > self.max_chars_per_line: + self.newline() + self.add_field(s) + else: + self.current_line = self.current_line + to_add + + def text(self): + return '\n'.join(self.lines) + + +def sample_text_monospaced_table(): + printables = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~ ' + table = SampleTable() + for i in range(6): + s = printables[i:] + printables[:i] + for k in range(0, len(printables), table.field_size): + table.add_field(s[k:k + table.field_size]) + table.newline() + return table.text() + + +# this worked right with all fonts in a win xp installation +def pyglet_safe(fontentry): + """ this is heuristic and conservative. YMMV. """ + return fontentry.vector and fontentry.family != wq.FF_DONTCARE + + +class Window(pyglet.window.Window): + font_num = 0 + + def on_text_motion(self, motion): + if motion == pyglet.window.key.MOTION_RIGHT: + self.font_num += 1 + if self.font_num == len(font_names): + self.font_num = 0 + elif motion == pyglet.window.key.MOTION_LEFT: + self.font_num -= 1 + if self.font_num < 0: + self.font_num = len(font_names) - 1 + + face = font_names[self.font_num] + self.head = pyglet.text.Label(face, font_size=16, y=0, anchor_y='bottom') + self.text = pyglet.text.Label(sample_text, font_name=face, font_size=12, + y=self.height, anchor_y='top', width=self.width, + multiline=True) + + def on_draw(self): + self.clear() + self.head.draw() + self.text.draw() + + +lorem_ipsum = """ +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. +Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec +consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget +libero egestas mattis sit amet vitae augue. + + +""" + +if __name__ == '__main__': + print(__doc__) + safe = True + sample_text = lorem_ipsum + sample_text_monospaced_table() + # all fonts known by the OS + fontdb = wq.query() + + if safe: + candidates = [f for f in fontdb if pyglet_safe(f)] + else: + canditates = fontdb + + # theres one fontentry for each charset supported, so reduce names + font_names = list(set([f.name for f in candidates])) + + font_names.sort() + window = Window(1024, 600) + window.on_text_motion(None) + pyglet.app.run() diff -Nru pyglet-1.4.10/examples/text/html_label.py pyglet-1.5.14/examples/text/html_label.py --- pyglet-1.4.10/examples/text/html_label.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/text/html_label.py 2020-11-18 13:03:44.000000000 +0000 @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""A simple demonstration of the HTMLLabel class, as it might be used on a +help or introductory screen. +""" + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import os +import pyglet + +html = ''' +

HTML labels in pyglet

+ +

+ +

HTML labels are a simple way to add formatted text to your application. +Different fonts, styles +and colours are supported. + +

This window has been made resizable; text will reflow to fit the new size. +''' + +window = pyglet.window.Window(resizable=True) +location = pyglet.resource.FileLocation(os.path.dirname(__file__)) +label = pyglet.text.HTMLLabel(html, location=location, + width=window.width, + multiline=True, anchor_y='center') + + +@window.event +def on_resize(width, height): + # Wrap text to the width of the window + label.width = window.width + + # Keep text vertically centered in the window + label.y = window.height // 2 + + +@window.event +def on_draw(): + window.clear() + label.draw() + + +pyglet.gl.glClearColor(1, 1, 1, 1) +pyglet.app.run() Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/examples/text/pyglet.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/examples/text/pyglet.png differ diff -Nru pyglet-1.4.10/examples/text/text_input.py pyglet-1.5.14/examples/text/text_input.py --- pyglet-1.4.10/examples/text/text_input.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/examples/text/text_input.py 2020-12-23 15:47:40.000000000 +0000 @@ -0,0 +1,148 @@ +#!/usr/bin/env python + +"""Demonstrates basic use of IncrementalTextLayout and Caret. + +A simple widget-like system is created in this example supporting keyboard and +mouse focus. +""" + +__docformat__ = 'restructuredtext' +__version__ = '$Id: $' + +import pyglet + + +class Rectangle: + """Draws a rectangle into a batch.""" + def __init__(self, x1, y1, x2, y2, batch): + self.vertex_list = batch.add(4, pyglet.gl.GL_QUADS, None, + ('v2i', [x1, y1, x2, y1, x2, y2, x1, y2]), + ('c4B', [200, 200, 220, 255] * 4)) + + +class TextWidget: + def __init__(self, text, x, y, width, batch): + self.document = pyglet.text.document.UnformattedDocument(text) + self.document.set_style(0, len(self.document.text), dict(color=(0, 0, 0, 255))) + font = self.document.get_font() + height = font.ascent - font.descent + + self.layout = pyglet.text.layout.IncrementalTextLayout( + self.document, width, height, multiline=False, batch=batch) + self.caret = pyglet.text.caret.Caret(self.layout) + + self.layout.x = x + self.layout.y = y + + # Rectangular outline + pad = 2 + self.rectangle = Rectangle(x - pad, y - pad, + x + width + pad, y + height + pad, batch) + + def hit_test(self, x, y): + return (0 < x - self.layout.x < self.layout.width and + 0 < y - self.layout.y < self.layout.height) + + +class Window(pyglet.window.Window): + def __init__(self, *args, **kwargs): + super(Window, self).__init__(400, 140, caption='Text entry') + + self.batch = pyglet.graphics.Batch() + self.labels = [ + pyglet.text.Label('Name', x=10, y=100, anchor_y='bottom', + color=(0, 0, 0, 255), batch=self.batch), + pyglet.text.Label('Species', x=10, y=60, anchor_y='bottom', + color=(0, 0, 0, 255), batch=self.batch), + pyglet.text.Label('Special abilities', x=10, y=20, + anchor_y='bottom', color=(0, 0, 0, 255), + batch=self.batch) + ] + self.widgets = [ + TextWidget('', 200, 100, self.width - 210, self.batch), + TextWidget('', 200, 60, self.width - 210, self.batch), + TextWidget('', 200, 20, self.width - 210, self.batch) + ] + self.text_cursor = self.get_system_mouse_cursor('text') + + self.focus = None + self.set_focus(self.widgets[0]) + + def on_resize(self, width, height): + super(Window, self).on_resize(width, height) + for widget in self.widgets: + widget.width = width - 110 + + def on_draw(self): + pyglet.gl.glClearColor(1, 1, 1, 1) + self.clear() + self.batch.draw() + + def on_mouse_motion(self, x, y, dx, dy): + for widget in self.widgets: + if widget.hit_test(x, y): + self.set_mouse_cursor(self.text_cursor) + break + else: + self.set_mouse_cursor(None) + + def on_mouse_press(self, x, y, button, modifiers): + for widget in self.widgets: + if widget.hit_test(x, y): + self.set_focus(widget) + break + else: + self.set_focus(None) + + if self.focus: + self.focus.caret.on_mouse_press(x, y, button, modifiers) + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + if self.focus: + self.focus.caret.on_mouse_drag(x, y, dx, dy, buttons, modifiers) + + def on_text(self, text): + if self.focus: + self.focus.caret.on_text(text) + + def on_text_motion(self, motion): + if self.focus: + self.focus.caret.on_text_motion(motion) + + def on_text_motion_select(self, motion): + if self.focus: + self.focus.caret.on_text_motion_select(motion) + + def on_key_press(self, symbol, modifiers): + if symbol == pyglet.window.key.TAB: + if modifiers & pyglet.window.key.MOD_SHIFT: + direction = -1 + else: + direction = 1 + + if self.focus in self.widgets: + i = self.widgets.index(self.focus) + else: + i = 0 + direction = 0 + + self.set_focus(self.widgets[(i + direction) % len(self.widgets)]) + + elif symbol == pyglet.window.key.ESCAPE: + pyglet.app.exit() + + def set_focus(self, focus): + if focus is self.focus: + return + + if self.focus: + self.focus.caret.visible = False + self.focus.caret.mark = self.focus.caret.position = 0 + + self.focus = focus + if self.focus: + self.focus.caret.visible = True + + +window = Window(resizable=True) +pyglet.app.run() diff -Nru pyglet-1.4.10/examples/text_input.py pyglet-1.5.14/examples/text_input.py --- pyglet-1.4.10/examples/text_input.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/examples/text_input.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -#!/usr/bin/env python - -"""Demonstrates basic use of IncrementalTextLayout and Caret. - -A simple widget-like system is created in this example supporting keyboard and -mouse focus. -""" - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import pyglet - - -class Rectangle(object): - """Draws a rectangle into a batch.""" - def __init__(self, x1, y1, x2, y2, batch): - self.vertex_list = batch.add(4, pyglet.gl.GL_QUADS, None, - ('v2i', [x1, y1, x2, y1, x2, y2, x1, y2]), - ('c4B', [200, 200, 220, 255] * 4)) - - -class TextWidget(object): - def __init__(self, text, x, y, width, batch): - self.document = pyglet.text.document.UnformattedDocument(text) - self.document.set_style(0, len(self.document.text), dict(color=(0, 0, 0, 255))) - font = self.document.get_font() - height = font.ascent - font.descent - - self.layout = pyglet.text.layout.IncrementalTextLayout( - self.document, width, height, multiline=False, batch=batch) - self.caret = pyglet.text.caret.Caret(self.layout) - - self.layout.x = x - self.layout.y = y - - # Rectangular outline - pad = 2 - self.rectangle = Rectangle(x - pad, y - pad, - x + width + pad, y + height + pad, batch) - - def hit_test(self, x, y): - return (0 < x - self.layout.x < self.layout.width and - 0 < y - self.layout.y < self.layout.height) - - -class Window(pyglet.window.Window): - def __init__(self, *args, **kwargs): - super(Window, self).__init__(400, 140, caption='Text entry') - - self.batch = pyglet.graphics.Batch() - self.labels = [ - pyglet.text.Label('Name', x=10, y=100, anchor_y='bottom', - color=(0, 0, 0, 255), batch=self.batch), - pyglet.text.Label('Species', x=10, y=60, anchor_y='bottom', - color=(0, 0, 0, 255), batch=self.batch), - pyglet.text.Label('Special abilities', x=10, y=20, - anchor_y='bottom', color=(0, 0, 0, 255), - batch=self.batch) - ] - self.widgets = [ - TextWidget('', 200, 100, self.width - 210, self.batch), - TextWidget('', 200, 60, self.width - 210, self.batch), - TextWidget('', 200, 20, self.width - 210, self.batch) - ] - self.text_cursor = self.get_system_mouse_cursor('text') - - self.focus = None - self.set_focus(self.widgets[0]) - - def on_resize(self, width, height): - super(Window, self).on_resize(width, height) - for widget in self.widgets: - widget.width = width - 110 - - def on_draw(self): - pyglet.gl.glClearColor(1, 1, 1, 1) - self.clear() - self.batch.draw() - - def on_mouse_motion(self, x, y, dx, dy): - for widget in self.widgets: - if widget.hit_test(x, y): - self.set_mouse_cursor(self.text_cursor) - break - else: - self.set_mouse_cursor(None) - - def on_mouse_press(self, x, y, button, modifiers): - for widget in self.widgets: - if widget.hit_test(x, y): - self.set_focus(widget) - break - else: - self.set_focus(None) - - if self.focus: - self.focus.caret.on_mouse_press(x, y, button, modifiers) - - def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - if self.focus: - self.focus.caret.on_mouse_drag(x, y, dx, dy, buttons, modifiers) - - def on_text(self, text): - if self.focus: - self.focus.caret.on_text(text) - - def on_text_motion(self, motion): - if self.focus: - self.focus.caret.on_text_motion(motion) - - def on_text_motion_select(self, motion): - if self.focus: - self.focus.caret.on_text_motion_select(motion) - - def on_key_press(self, symbol, modifiers): - if symbol == pyglet.window.key.TAB: - if modifiers & pyglet.window.key.MOD_SHIFT: - direction = -1 - else: - direction = 1 - - if self.focus in self.widgets: - i = self.widgets.index(self.focus) - else: - i = 0 - direction = 0 - - self.set_focus(self.widgets[(i + direction) % len(self.widgets)]) - - elif symbol == pyglet.window.key.ESCAPE: - pyglet.app.exit() - - def set_focus(self, focus): - if focus is self.focus: - return - - if self.focus: - self.focus.caret.visible = False - self.focus.caret.mark = self.focus.caret.position = 0 - - self.focus = focus - if self.focus: - self.focus.caret.visible = True - - -window = Window(resizable=True) -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/timer.py pyglet-1.5.14/examples/timer.py --- pyglet-1.4.10/examples/timer.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/timer.py 2020-11-20 14:28:24.000000000 +0000 @@ -47,7 +47,7 @@ window = pyglet.window.Window(fullscreen=True) -class Timer(object): +class Timer: def __init__(self): self.label = pyglet.text.Label('00:00', font_size=360, x=window.width//2, y=window.height//2, diff -Nru pyglet-1.4.10/examples/video.py pyglet-1.5.14/examples/video.py --- pyglet-1.4.10/examples/video.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/video.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""Simple example of video playback. - -Usage:: - - video.py - -See the Programming Guide for a partial list of supported video formats. -""" - -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -import sys -import pyglet - -if len(sys.argv) < 2: - print(__doc__) - sys.exit(1) - -source = pyglet.media.load(sys.argv[1]) -fmt = source.video_format -if not fmt: - print('No video track in this source.') - sys.exit(1) - -player = pyglet.media.Player() -player.queue(source) -player.play() - -window = pyglet.window.Window(width=fmt.width, height=fmt.height) - - -@window.event -def on_draw(): - player.texture.blit(0, 0) - - -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/window_events.py pyglet-1.5.14/examples/window_events.py --- pyglet-1.4.10/examples/window_events.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/window_events.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""Prints all window events to stdout. -""" - -import pyglet - -window = pyglet.window.Window(resizable=True) - - -@window.event -def on_draw(): - window.clear() - - -# A logger class, which prints events to stdout or to a file: -win_event_logger = pyglet.window.event.WindowEventLogger() -window.push_handlers(win_event_logger) - -pyglet.app.run() diff -Nru pyglet-1.4.10/examples/window_platform_event.py pyglet-1.5.14/examples/window_platform_event.py --- pyglet-1.4.10/examples/window_platform_event.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/examples/window_platform_event.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -#!/usr/bin/env python -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -"""Demonstrates how to handle a platform-specific event not defined in -pyglet by subclassing Window. This is not for the faint-hearted! - -A message will be printed to stdout when the following events are caught: - - - On Mac OS X, the window drag region is clicked. - - On Windows, the display resolution is changed. - - On Linux, the window properties are changed. - -""" - -from __future__ import print_function - -import pyglet - - -_have_cocoa = _have_win32 = _have_xlib = False - -if pyglet.compat_platform.startswith('linux'): - _have_xlib = True - from pyglet.window.xlib import * - -elif pyglet.compat_platform == 'darwin': - _have_cocoa = True - from pyglet.window.cocoa import * - -elif pyglet.compat_platform in ('win32', 'cygwin'): - _have_win32 = True - from pyglet.window.win32 import * - - -# Subclass Window -class MyWindow(pyglet.window.Window): - if _have_cocoa: - # TODO This is currently not supported in Cocoa (#156). - pass - - if _have_win32: - @Win32EventHandler(WM_DISPLAYCHANGE) - def _on_window_display_change(self, msg, lParam, wParam): - print('Display resolution changed.') - return 0 - - if _have_xlib: - @XlibEventHandler(xlib.PropertyNotify) - def _on_window_property_notify(self, event): - print('Property notify.') - - -if __name__ == '__main__': - window = MyWindow() - pyglet.app.run() diff -Nru pyglet-1.4.10/PKG-INFO pyglet-1.5.14/PKG-INFO --- pyglet-1.4.10/PKG-INFO 2020-01-17 18:37:44.000000000 +0000 +++ pyglet-1.5.14/PKG-INFO 2020-12-31 21:13:06.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pyglet -Version: 1.4.10 +Version: 1.5.14 Summary: Cross-platform windowing and multimedia library Home-page: http://pyglet.readthedocs.org/en/latest/ Author: Alex Holkner @@ -30,7 +30,7 @@ Pyglet has an active developer and user community. If you find a bug or a problem with the documentation, please [open an issue](https://github.com/pyglet/pyglet/issues). - Anyone is welcome to join our [discord] server were a lot of the development discussion is going on. + Anyone is welcome to join our [discord] server where a lot of the development discussion is going on. It's also a great place to ask for help. Some of the features of pyglet are: @@ -41,33 +41,31 @@ * **Take advantage of multiple windows and multi-monitor desktops.** *pyglet* allows you to use multiple platform-native windows, and is fully aware of multi-monitor setups for use with fullscreen games. * **Load images, sound, music and video in almost any format.** *pyglet* can optionally use FFmpeg to play back - audio formats such as MP3, OGG/Vorbis and WMA, and video formats such as DivX, MPEG-2, H.264, WMV and Xvid. + audio formats such as MP3, OGG/Vorbis and WMA, and video formats such as MPEG2, H.264, H.265, WMV and Xvid. Without FFmpeg, *pyglet* contains built-in support for standard formats such as wav, png, bmp, and others. * **pyglet is written entirely in pure Python**, and makes use of the *ctypes* module to interface with system libraries. You can modify the codebase or make a contribution without any second language compilation steps or compiler setup. Despite being pure Python, *pyglet* has excellent performance thanks to advanced batching for - drawing thousands of sprites or animations. + drawing thousands of objects. * **pyglet is provided under the BSD open-source license**, allowing you to use it for both commercial and other open-source projects with very little restriction. ## Requirements - pyglet runs under Python 2.7, and 3.4+. The entire codebase is fully 2/3 dual - compatible, making use of the future module for backwards compatibility with - legacy Python. Being written in pure Python, it also works on other Python + Pyglet runs under Python 3.5+. Being written in pure Python, it also works on other Python interpreters such as PyPy. Supported platforms are: - * Windows XP or later + * Windows 7 or later * Mac OS X 10.3 or later * Linux, with the following libraries (most recent distributions will have these in a default installation): * OpenGL and GLX - * GDK 2.0+ or PIL (required for loading images other than PNG and BMP) + * GDK 2.0+ or Pillow (required for loading images other than PNG and BMP) * OpenAL or Pulseaudio (required for playing audio) - **Please note that pyglet v1.4 will likely be the last version to support - Python 2.7**. Future releases of pyglet will be Python 3 only, and will be - targeting OpenGL 3.3+. Previous releases will remain available for download. + **Please note that pyglet v1.5 will likely be the last version to support + legacy OpenGL**. Future releases of pyglet will be targeting OpenGL 3.3+. + Previous releases will remain available for download. Starting with version 1.4, to play compressed audio and video files, you will also need [FFmpeg](https://ffmpeg.org/). @@ -76,7 +74,7 @@ pyglet is installable from PyPI: - pip install --upgrade pyglet --user + pip install --upgrade --user pyglet ## Installation from source @@ -86,7 +84,7 @@ You can also install the latest development version direct from Github using: - pip install --upgrade https://github.com/pyglet/pyglet/archive/master.zip --user + pip install --upgrade --user https://github.com/pyglet/pyglet/archive/master.zip For local development install pyglet in editable mode: @@ -107,7 +105,7 @@ are going to work with, also read the associated docs. If you don't understand the code with the help of the docs, it is a sign that the docs should be improved. - If you want to contribute to Pyglet, we suggest the following: + If you want to contribute to pyglet, we suggest the following: * Fork the [official repository](https://github.com/pyglet/pyglet/fork). * Apply your changes to your fork. @@ -166,12 +164,11 @@ Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX :: Linux -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 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: Topic :: Games/Entertainment Classifier: Topic :: Software Development :: Libraries :: Python Modules Description-Content-Type: text/markdown diff -Nru pyglet-1.4.10/pyglet/app/base.py pyglet-1.5.14/pyglet/app/base.py --- pyglet-1.4.10/pyglet/app/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/app/base.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,37 +32,32 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import print_function -from __future__ import division -from future import standard_library -standard_library.install_aliases() -from builtins import next -from builtins import object -import platform -import queue import sys +import queue +import platform import threading from pyglet import app -from pyglet import compat_platform from pyglet import clock from pyglet import event +from pyglet import compat_platform _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run -class PlatformEventLoop(object): +class PlatformEventLoop: """ Abstract class, implementation depends on platform. .. versionadded:: 1.2 """ + def __init__(self): self._event_queue = queue.Queue() self._is_running = threading.Event() self._is_running.clear() - def is_running(self): + def is_running(self): """Return True if the event loop is currently processing, or False if it is blocked or not activated. @@ -166,87 +161,21 @@ platform_event_loop = app.platform_event_loop platform_event_loop.start() self.dispatch_event('on_enter') - self.is_running = True - legacy_platforms = ('XP', '2000', '2003Server', 'post2003') - if compat_platform == 'win32' and platform.win32_ver()[0] in legacy_platforms: - self._run_estimated() - else: - self._run() - self.is_running = False - self.dispatch_event('on_exit') - platform_event_loop.stop() - - def _run(self): - """The simplest standard run loop, using constant timeout. Suitable - for well-behaving platforms (Mac, Linux and some Windows). - """ - platform_event_loop = app.platform_event_loop while not self.has_exit: timeout = self.idle() platform_event_loop.step(timeout) - def _run_estimated(self): - """Run-loop that continually estimates function mapping requested - timeout to measured timeout using a least-squares linear regression. - Suitable for oddball platforms (Windows). - - XXX: There is no real relation between the timeout given by self.idle(), and used - to calculate the estimate, and the time actually spent waiting for events. I have - seen this cause a negative gradient, showing a negative relation. Then CPU use - runs out of control due to very small estimates. - """ - platform_event_loop = app.platform_event_loop - - predictor = self._least_squares() - gradient, offset = next(predictor) - - time = self.clock.time - while not self.has_exit: - timeout = self.idle() - if timeout is None: - estimate = None - else: - estimate = max(gradient * timeout + offset, 0.0) - if False: - print('Gradient = %f, Offset = %f' % (gradient, offset)) - print('Timeout = %f, Estimate = %f' % (timeout, estimate)) - - t = time() - if not platform_event_loop.step(estimate) and estimate != 0.0 and estimate is not None: - dt = time() - t - gradient, offset = predictor.send((dt, estimate)) - - @staticmethod - def _least_squares(gradient=1, offset=0): - X = 0 - Y = 0 - XX = 0 - XY = 0 - n = 0 - - while True: - x, y = yield gradient, offset - X += x - Y += y - XX += x * x - XY += x * y - n += 1 - - try: - gradient = (n * XY - X * Y) / (n * XX - X * X) - offset = (Y - gradient * X) / n - except ZeroDivisionError: - # Can happen in pathalogical case; keep current - # gradient/offset for now. - pass + self.is_running = False + self.dispatch_event('on_exit') + platform_event_loop.stop() def _legacy_setup(self): # Disable event queuing for dispatch_events from pyglet.window import Window Window._enable_event_queue = False - + # Dispatch pending events for window in app.windows: window.switch_to() diff -Nru pyglet-1.4.10/pyglet/app/cocoa.py pyglet-1.5.14/pyglet/app/cocoa.py --- pyglet-1.4.10/pyglet/app/cocoa.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/app/cocoa.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,8 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' from pyglet.app.base import PlatformEventLoop from pyglet.libs.darwin import cocoapy @@ -47,7 +45,7 @@ NSUserDefaults = cocoapy.ObjCClass('NSUserDefaults') -class AutoReleasePool(object): +class AutoReleasePool: def __enter__(self): self.pool = NSAutoreleasePool.alloc().init() return self.pool diff -Nru pyglet-1.4.10/pyglet/app/__init__.py pyglet-1.5.14/pyglet/app/__init__.py --- pyglet-1.4.10/pyglet/app/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/app/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -63,10 +63,6 @@ .. versionadded:: 1.1 """ -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' import sys import weakref @@ -92,40 +88,7 @@ pass -class WeakSet(object): - """Set of objects, referenced weakly. - - Adding an object to this set does not prevent it from being garbage - collected. Upon being garbage collected, the object is automatically - removed from the set. - """ - - def __init__(self): - self._dict = weakref.WeakKeyDictionary() - - def add(self, value): - self._dict[value] = True - - def remove(self, value): - # Value might be removed already if this is during __del__ of the item. - self._dict.pop(value, None) - - def pop(self): - value, _ = self._dict.popitem() - return value - - def __iter__(self): - for key in self._dict.keys(): - yield key - - def __contains__(self, other): - return other in self._dict - - def __len__(self): - return len(self._dict) - - -windows = WeakSet() +windows = weakref.WeakSet() """Set of all open windows (including invisible windows). Instances of :class:`pyglet.window.Window` are automatically added to this set upon construction. The set uses weak references, so windows are removed from diff -Nru pyglet-1.4.10/pyglet/app/win32.py pyglet-1.5.14/pyglet/app/win32.py --- pyglet-1.4.10/pyglet/app/win32.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/app/win32.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -from __future__ import absolute_import # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -33,8 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' from .base import PlatformEventLoop diff -Nru pyglet-1.4.10/pyglet/app/xlib.py pyglet-1.5.14/pyglet/app/xlib.py --- pyglet-1.4.10/pyglet/app/xlib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/app/xlib.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,10 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -from builtins import object import os import select @@ -43,10 +39,10 @@ from pyglet import app from pyglet.app.base import PlatformEventLoop -from pyglet.compat import asbytes +from pyglet.util import asbytes -class XlibSelectDevice(object): +class XlibSelectDevice: def fileno(self): """Get the file handle for ``select()`` for this device. diff -Nru pyglet-1.4.10/pyglet/canvas/base.py pyglet-1.5.14/pyglet/canvas/base.py --- pyglet-1.4.10/pyglet/canvas/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/canvas/base.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,17 +32,14 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from builtins import object -# !/usr/bin/python -# $Id:$ -from pyglet import app from pyglet import gl +from pyglet import app from pyglet import window from pyglet import canvas -class Display(object): +class Display: """A display device supporting one or more screens. .. versionadded:: 1.2 @@ -116,7 +113,7 @@ return [window for window in app.windows if window.display is self] -class Screen(object): +class Screen: """A virtual monitor that supports fullscreen windows. Screens typically map onto a physical display such as a @@ -301,7 +298,7 @@ raise NotImplementedError('abstract') -class ScreenMode(object): +class ScreenMode: """Screen resolution and display settings. Applications should not construct `ScreenMode` instances themselves; see @@ -349,7 +346,7 @@ self.width, self.height, self.depth, self.rate) -class Canvas(object): +class Canvas: """Abstract drawing area. Canvases are used internally by pyglet to represent drawing areas -- diff -Nru pyglet-1.4.10/pyglet/canvas/cocoa.py pyglet-1.5.14/pyglet/canvas/cocoa.py --- pyglet-1.4.10/pyglet/canvas/cocoa.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/canvas/cocoa.py 2020-11-10 10:36:26.000000000 +0000 @@ -34,21 +34,14 @@ # ---------------------------------------------------------------------------- # Note: The display mode API used here is Mac OS 10.6 only. -''' -''' -from __future__ import absolute_import - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - from ctypes import * -from pyglet import app from .base import Display, Screen, ScreenMode, Canvas from pyglet.libs.darwin.cocoapy import CGDirectDisplayID, quartz, cf from pyglet.libs.darwin.cocoapy import cfstring_to_string, cfarray_to_list + class CocoaDisplay(Display): def get_screens(self): @@ -101,7 +94,7 @@ def get_modes(self): cgmodes = c_void_p(quartz.CGDisplayCopyAllDisplayModes(self._cg_display_id, None)) - modes = [ CocoaScreenMode(self, cgmode) for cgmode in cfarray_to_list(cgmodes) ] + modes = [CocoaScreenMode(self, cgmode) for cgmode in cfarray_to_list(cgmodes)] cf.CFRelease(cgmodes) return modes diff -Nru pyglet-1.4.10/pyglet/canvas/__init__.py pyglet-1.5.14/pyglet/canvas/__init__.py --- pyglet-1.4.10/pyglet/canvas/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/canvas/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -60,14 +60,13 @@ """ import sys - -from pyglet.app import WeakSet +import weakref _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run -_displays = WeakSet() +_displays = weakref.WeakSet() """Set of all open displays. Instances of :class:`Display` are automatically added to this set upon construction. The set uses weak references, so displays are removed from the set when they are no longer referenced. diff -Nru pyglet-1.4.10/pyglet/canvas/win32.py pyglet-1.5.14/pyglet/canvas/win32.py --- pyglet-1.4.10/pyglet/canvas/win32.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/canvas/win32.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -33,18 +32,18 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ -from __future__ import absolute_import from .base import Display, Screen, ScreenMode, Canvas from pyglet.libs.win32 import _kernel32, _user32, types, constants from pyglet.libs.win32.constants import * from pyglet.libs.win32.types import * + class Win32Display(Display): def get_screens(self): screens = [] + def enum_proc(hMonitor, hdcMonitor, lprcMonitor, dwData): r = lprcMonitor.contents width = r.right - r.left @@ -52,10 +51,12 @@ screens.append( Win32Screen(self, hMonitor, r.left, r.top, width, height)) return True + enum_proc_ptr = MONITORENUMPROC(enum_proc) _user32.EnumDisplayMonitors(None, None, enum_proc_ptr, 0) return screens + class Win32Screen(Screen): _initial_mode = None @@ -97,7 +98,7 @@ mode = DEVMODE() mode.dmSize = sizeof(DEVMODE) _user32.EnumDisplaySettingsW(self.get_device_name(), - ENUM_CURRENT_SETTINGS, + ENUM_CURRENT_SETTINGS, byref(mode)) return Win32ScreenMode(self, mode) @@ -119,6 +120,7 @@ if self._initial_mode: self.set_mode(self._initial_mode) + class Win32ScreenMode(ScreenMode): def __init__(self, screen, mode): super(Win32ScreenMode, self).__init__(screen) @@ -128,9 +130,9 @@ self.depth = mode.dmBitsPerPel self.rate = mode.dmDisplayFrequency + class Win32Canvas(Canvas): def __init__(self, display, hwnd, hdc): super(Win32Canvas, self).__init__(display) self.hwnd = hwnd self.hdc = hdc - diff -Nru pyglet-1.4.10/pyglet/canvas/xlib.py pyglet-1.5.14/pyglet/canvas/xlib.py --- pyglet-1.4.10/pyglet/canvas/xlib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/canvas/xlib.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -34,17 +33,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from __future__ import print_function -from __future__ import absolute_import -from builtins import range - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -from ctypes import * import ctypes +from ctypes import * from pyglet import app from pyglet.app.xlib import XlibSelectDevice @@ -52,30 +42,37 @@ from . import xlib_vidmoderestore + # XXX -#from pyglet.window import NoSuchDisplayException +# from pyglet.window import NoSuchDisplayException class NoSuchDisplayException(Exception): pass + from pyglet.libs.x11 import xlib + try: from pyglet.libs.x11 import xinerama + _have_xinerama = True except: _have_xinerama = False try: from pyglet.libs.x11 import xsync + _have_xsync = True except: _have_xsync = False try: from pyglet.libs.x11 import xf86vmode + _have_xf86vmode = True except: _have_xf86vmode = False + # Set up error handler def _error_handler(display, event): # By default, all errors are silently ignored: this has a better chance @@ -100,16 +97,19 @@ print('Python stack trace (innermost last):') traceback.print_stack() return 0 + + _error_handler_ptr = xlib.XErrorHandler(_error_handler) xlib.XSetErrorHandler(_error_handler_ptr) + class XlibDisplay(XlibSelectDevice, Display): - _display = None # POINTER(xlib.Display) + _display = None # POINTER(xlib.Display) - _x_im = None # X input method - # TODO close _x_im when display connection closed. + _x_im = None # X input method + # TODO close _x_im when display connection closed. _enable_xsync = False - _screens = None # Cache of get_screens() + _screens = None # Cache of get_screens() def __init__(self, name=None, x_screen=None): if x_screen is None: @@ -138,7 +138,7 @@ if _have_xsync: event_base = c_int() error_base = c_int() - if xsync.XSyncQueryExtension(self._display, + if xsync.XSyncQueryExtension(self._display, byref(event_base), byref(error_base)): major_version = c_int() @@ -157,10 +157,10 @@ if _have_xinerama and xinerama.XineramaIsActive(self._display): number = c_int() - infos = xinerama.XineramaQueryScreens(self._display, + infos = xinerama.XineramaQueryScreens(self._display, byref(number)) - infos = cast(infos, - POINTER(xinerama.XineramaScreenInfo * number.value)).contents + infos = cast(infos, + POINTER(xinerama.XineramaScreenInfo * number.value)).contents self._screens = [] using_xinerama = number.value > 1 for info in infos: @@ -207,13 +207,14 @@ def poll(self): return xlib.XPending(self._display) + class XlibScreen(Screen): _initial_mode = None def __init__(self, display, x, y, width, height, xinerama): super(XlibScreen, self).__init__(display, x, y, width, height) self._xinerama = xinerama - + def get_matching_configs(self, template): canvas = XlibCanvas(self.display, None) configs = template.match(canvas) @@ -242,8 +243,8 @@ modes = [] for i in range(count.value): info = xf86vmode.XF86VidModeModeInfo() - ctypes.memmove(ctypes.byref(info), - ctypes.byref(info_array.contents[i]), + ctypes.memmove(ctypes.byref(info), + ctypes.byref(info_array.contents[i]), ctypes.sizeof(info)) modes.append(XlibScreenMode(self, info)) if info.privsize: @@ -265,11 +266,9 @@ self._initial_mode = self.get_mode() xlib_vidmoderestore.set_initial_mode(self._initial_mode) - xf86vmode.XF86VidModeSwitchToMode(self.display._display, - self.display.x_screen, mode.info) + xf86vmode.XF86VidModeSwitchToMode(self.display._display, self.display.x_screen, mode.info) xlib.XFlush(self.display._display) - xf86vmode.XF86VidModeSetViewPort(self.display._display, - self.display.x_screen, 0, 0) + xf86vmode.XF86VidModeSetViewPort(self.display._display, self.display.x_screen, 0, 0) xlib.XFlush(self.display._display) self.width = mode.width @@ -282,8 +281,9 @@ def __repr__(self): return 'XlibScreen(display=%r, x=%d, y=%d, ' \ 'width=%d, height=%d, xinerama=%d)' % \ - (self.display, self.x, self.y, self.width, self.height, - self._xinerama) + (self.display, self.x, self.y, self.width, self.height, + self._xinerama) + class XlibScreenMode(ScreenMode): def __init__(self, screen, info): @@ -294,6 +294,7 @@ self.rate = info.dotclock self.depth = None + class XlibCanvas(Canvas): def __init__(self, display, x_window): super(XlibCanvas, self).__init__(display) diff -Nru pyglet-1.4.10/pyglet/canvas/xlib_vidmoderestore.py pyglet-1.5.14/pyglet/canvas/xlib_vidmoderestore.py --- pyglet-1.4.10/pyglet/canvas/xlib_vidmoderestore.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/canvas/xlib_vidmoderestore.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -33,9 +32,8 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id: $ -'''Fork a child process and inform it of mode changes to each screen. The +"""Fork a child process and inform it of mode changes to each screen. The child waits until the parent process dies, and then connects to each X server with a mode change and restores the mode. @@ -45,8 +43,7 @@ The child process is communicated to via a pipe, and watches for parent death with a Linux extension signal handler. -''' -from builtins import object +""" import ctypes import os @@ -55,7 +52,8 @@ import threading from pyglet.libs.x11 import xlib -from pyglet.compat import asbytes +from pyglet.util import asbytes + try: from pyglet.libs.x11 import xf86vmode except: @@ -66,6 +64,7 @@ _restorable_screens = set() _mode_write_pipe = None + # Mode packets tell the child process how to restore a given display and # screen. Only one packet should be sent per display/screen (more would # indicate redundancy or incorrect restoration). Packet format is: @@ -74,9 +73,10 @@ # width # height # rate -class ModePacket(object): +class ModePacket: format = '256siHHI' size = struct.calcsize(format) + def __init__(self, display, screen, width, height, rate): self.display = display self.screen = screen @@ -85,13 +85,11 @@ self.rate = rate def encode(self): - return struct.pack(self.format, self.display, self.screen, - self.width, self.height, self.rate) + return struct.pack(self.format, self.display, self.screen, self.width, self.height, self.rate) @classmethod def decode(cls, data): - display, screen, width, height, rate = \ - struct.unpack(cls.format, data) + display, screen, width, height, rate = struct.unpack(cls.format, data) return cls(display.strip(asbytes('\0')), screen, width, height, rate) def __repr__(self): @@ -102,29 +100,31 @@ def set(self): display = xlib.XOpenDisplay(self.display) modes, n_modes = get_modes_array(display, self.screen) - mode = get_matching_mode(modes, n_modes, - self.width, self.height, self.rate) + mode = get_matching_mode(modes, n_modes, self.width, self.height, self.rate) if mode is not None: xf86vmode.XF86VidModeSwitchToMode(display, self.screen, mode) free_modes_array(modes, n_modes) xlib.XCloseDisplay(display) + def get_modes_array(display, screen): count = ctypes.c_int() modes = ctypes.POINTER(ctypes.POINTER(xf86vmode.XF86VidModeModeInfo))() xf86vmode.XF86VidModeGetAllModeLines(display, screen, count, modes) return modes, count.value + def get_matching_mode(modes, n_modes, width, height, rate): # Copy modes out of list and free list for i in range(n_modes): mode = modes.contents[i] - if (mode.hdisplay == width and - mode.vdisplay == height and - mode.dotclock == rate): + if (mode.hdisplay == width and + mode.vdisplay == height and + mode.dotclock == rate): return mode return None + def free_modes_array(modes, n_modes): for i in range(n_modes): mode = modes.contents[i] @@ -132,6 +132,7 @@ xlib.XFree(mode.private) xlib.XFree(modes) + def _install_restore_mode_child(): global _mode_write_pipe global _restore_mode_child_installed @@ -157,8 +158,9 @@ # stops reading from the mode packet pipe and restores video modes on # all displays/screens it knows about. def _sighup(signum, frame): - parent_wait_lock.release(); - parent_wait_lock = threading.Lock(); + parent_wait_lock.release() + + parent_wait_lock = threading.Lock() parent_wait_lock.acquire() signal.signal(signal.SIGHUP, _sighup) @@ -175,12 +177,12 @@ packets.append(packet) buffer = buffer[ModePacket.size:] except OSError: - pass # Interrupted system call + pass # Interrupted system call for packet in packets: packet.set() os._exit(0) - + else: # Parent process. Clean up pipe then continue running program as # normal. Send mode packets through pipe as additional @@ -188,6 +190,7 @@ os.close(mode_read_pipe) _restore_mode_child_installed = True + def set_initial_mode(mode): _install_restore_mode_child() @@ -202,5 +205,3 @@ os.write(_mode_write_pipe, packet.encode()) _restorable_screens.add((display, screen)) - - diff -Nru pyglet-1.4.10/pyglet/clock.py pyglet-1.5.14/pyglet/clock.py --- pyglet-1.4.10/pyglet/clock.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/clock.py 2020-12-23 15:47:40.000000000 +0000 @@ -110,63 +110,17 @@ "wall-time", or to synchronise your clock to an audio or video stream instead of the system clock. """ -from __future__ import print_function -from __future__ import division -from builtins import range -from builtins import object -import sys import time -import ctypes + from operator import attrgetter from heapq import heappush, heappop, heappushpop from collections import deque -import pyglet.lib from pyglet import compat_platform -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - - -if sys.version_info[:2] < (3, 5): - # PYTHON2 - remove these legacy classes: - - if compat_platform in ('win32', 'cygwin'): - - class _ClockBase(object): - - @staticmethod - def sleep(microseconds): - time.sleep(microseconds * 1e-6) - - _default_time_function = time.clock - - else: - _c = pyglet.lib.load_library('c') - _c.usleep.argtypes = [ctypes.c_ulong] - - class _ClockBase(object): - - @staticmethod - def sleep(microseconds): - _c.usleep(int(microseconds)) - - _default_time_function = time.time - -else: - - class _ClockBase(object): - - @staticmethod - def sleep(microseconds): - time.sleep(microseconds * 1e-6) - - _default_time_function = time.perf_counter - - -class _ScheduledItem(object): +class _ScheduledItem: __slots__ = ['func', 'args', 'kwargs'] def __init__(self, func, args, kwargs): @@ -175,7 +129,7 @@ self.kwargs = kwargs -class _ScheduledIntervalItem(object): +class _ScheduledIntervalItem: __slots__ = ['func', 'interval', 'last_ts', 'next_ts', 'args', 'kwargs'] def __init__(self, func, interval, last_ts, next_ts, args, kwargs): @@ -193,7 +147,7 @@ return self.next_ts < other -class Clock(_ClockBase): +class Clock: """Class for calculating and limiting framerate. It is also used for calling scheduled functions. @@ -205,6 +159,7 @@ #: busy-waiting. Lower values mean the process sleeps more often, but is #: prone to over-sleep and run at a potentially lower or uneven framerate #: than desired. + #: On Windows, MIN_SLEEP is larger because the default timer resolution #: is set by default to 15 .6 ms. MIN_SLEEP = 0.008 if compat_platform in ('win32', 'cygwin') else 0.005 @@ -222,7 +177,7 @@ # If True, a sleep(0) is inserted on every tick. _force_sleep = False - def __init__(self, time_function=_default_time_function): + def __init__(self, time_function=time.perf_counter): """Initialise a Clock, with optional custom time function. :Parameters: @@ -246,6 +201,10 @@ self._schedule_interval_items = [] self._current_interval_item = None + @staticmethod + def sleep(microseconds): + time.sleep(microseconds * 1e-6) + def update_time(self): """Get the elapsed time since the last call to `update_time`. @@ -699,9 +658,9 @@ The result is the sliding average of the last "n" updates, where "n" is some number designed to cover approximately 1 - second. This is **not** the Window redraw rate. Platform - events, such as moving the mouse rapidly, will cause the - clock to refresh more often + second. This is the internal clock update rate, **not** the + Window redraw rate. Platform events, such as moving the + mouse rapidly, will cause the clock to refresh more often. :rtype: float :return: The measured updates per second. diff -Nru pyglet-1.4.10/pyglet/compat.py pyglet-1.5.14/pyglet/compat.py --- pyglet-1.4.10/pyglet/compat.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/compat.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,163 +0,0 @@ -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# Copyright (c) 2008-2020 pyglet contributors -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- - -'''Compatibility tools - -Various tools for simultaneous Python 2.x and Python 3.x support -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -import sys -import itertools -import weakref - -if sys.version_info[0] == 2: - if sys.version_info[1] < 6: - # Pure Python implementation from - # http://docs.python.org/library/itertools.html#itertools.izip_longest - def izip_longest(*args, **kwds): - # izip_longest('ABCD', 'xy', fillvalue='-') --> Ax By C- D- - fillvalue = kwds.get('fillvalue') - def sentinel(counter=([fillvalue] * (len(args) - 1)).pop): - yield counter() # yields the fillvalue, or raises IndexError - fillers = itertools.repeat(fillvalue) - iters = [itertools.chain(it, sentinel(), fillers) for it in args] - try: - for tup in itertools.izip(*iters): - yield tup - except IndexError: - pass - else: - izip_longest = itertools.izip_longest -else: - izip_longest = itertools.zip_longest - - -if sys.version_info[0] >= 3: - import io - - def asbytes(s): - if isinstance(s, bytes): - return s - elif isinstance(s, str): - return bytes(ord(c) for c in s) - else: - return bytes(s) - - def asbytes_filename(s): - if isinstance(s, bytes): - return s - elif isinstance(s, str): - return s.encode(encoding=sys.getfilesystemencoding()) - - def asstr(s): - if s is None: - return '' - if isinstance(s, str): - return s - return s.decode("utf-8") - - bytes_type = bytes - BytesIO = io.BytesIO -else: - import StringIO - - asbytes = str - asbytes_filename = str - asstr = str - bytes_type = str - BytesIO = StringIO.StringIO - - -# Backporting for Python < 3.4 -class _WeakMethod(weakref.ref): - """ - A custom 'weakref.ref' subclass which simulates a weak reference to - a bound method, working around the lifetime problem of bound methods - """ - - __slots__ = '_func_ref', '_meth_type', '_alive', '__weakref__' - - def __new__(cls, meth, callback=None): - try: - obj = meth.__self__ - func = meth.__func__ - except AttributeError: - raise TypeError('argument should be a bound method, not {0}'.format(type(meth))) - - def _cb(arg): - # The self-weakref trick is needed to avoid creating a reference cycle. - self = self_wr() - if self._alive: - self._alive = False - if callback is not None: - callback(self) - self = weakref.ref.__new__(cls, obj, _cb) - self._func_ref = weakref.ref(func, _cb) - self._meth_type = type(meth) - self._alive = True - self_wr = weakref.ref(self) - return self - - def __call__(self): - obj = super(WeakMethod, self).__call__() - func = self._func_ref() - if obj is None or func is None: - return None - return self._meth_type(func, obj) - - def __eq__(self, other): - if isinstance(other, WeakMethod): - if not self._alive or not other._alive: - return self is other - return weakref.ref.__eq__(self, other) and self._func_ref == other._func_ref - return False - - def __ne__(self, other): - if isinstance(other, WeakMethod): - if not self._alive or not other._alive: - return self is not other - return weakref.ref.__ne__(self, other) or self._func_ref != other._func_ref - return True - - __hash__ = weakref.ref.__hash__ - - -if sys.version_info < (3, 4): - WeakMethod = _WeakMethod -else: - from weakref import WeakMethod diff -Nru pyglet-1.4.10/pyglet/com.py pyglet-1.5.14/pyglet/com.py --- pyglet-1.4.10/pyglet/com.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/com.py 2020-12-31 21:03:32.000000000 +0000 @@ -33,20 +33,20 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Minimal Windows COM interface. +"""Minimal Windows COM interface. Allows pyglet to use COM interfaces on Windows without comtypes. Unlike comtypes, this module does not provide property interfaces, read typelibs, -nice-ify return values or permit Python implementations of COM interfaces. We -don't need anything that sophisticated to work with DirectX. +nice-ify return values. We don't need anything that sophisticated to work with COM's. -All interfaces should derive from IUnknown (defined in this module). The -Python COM interfaces are actually pointers to the implementation (take note +Interfaces should derive from pIUnknown if their implementation is returned by the COM. +The Python COM interfaces are actually pointers to the implementation (take note when translating methods that take an interface as argument). +(example: A Double Pointer is simply POINTER(MyInterface) as pInterface is already a POINTER.) Interfaces can define methods:: - class IDirectSound8(com.IUnknown): + class IDirectSound8(com.pIUnknown): _methods_ = [ ('CreateSoundBuffer', com.STDMETHOD()), ('GetCaps', com.STDMETHOD(LPDSCAPS)), @@ -67,19 +67,19 @@ Don't forget to manually manage memory... call Release() when you're done with an interface. -''' -from builtins import object +""" -import ctypes import sys +import ctypes -from pyglet.debug import debug_print +from pyglet.util import debug_print _debug_com = debug_print('debug_com') if sys.platform != 'win32': raise ImportError('pyglet.com requires a Windows build of Python') + class GUID(ctypes.Structure): _fields_ = [ ('Data1', ctypes.c_ulong), @@ -99,26 +99,45 @@ return 'GUID(%x, %x, %x, %x, %x, %x, %x, %x, %x, %x, %x)' % ( self.Data1, self.Data2, self.Data3, b1, b2, b3, b4, b5, b6, b7, b8) + def __cmp__(self, other): + if isinstance(other, GUID): + return ctypes.cmp(bytes(self), bytes(other)) + return -1 + + def __eq__(self, other): + return isinstance(other, GUID) and bytes(self) == bytes(other) + + def __hash__(self): + return hash(bytes(self)) + + LPGUID = ctypes.POINTER(GUID) IID = GUID REFIID = ctypes.POINTER(IID) -class METHOD(object): - '''COM method.''' + +class METHOD: + """COM method.""" + def __init__(self, restype, *args): self.restype = restype self.argtypes = args def get_field(self): + # ctypes caches WINFUNCTYPE's so this should be ok. return ctypes.WINFUNCTYPE(self.restype, *self.argtypes) + class STDMETHOD(METHOD): - '''COM method with HRESULT return value.''' + """COM method with HRESULT return value.""" + def __init__(self, *args): super(STDMETHOD, self).__init__(ctypes.HRESULT, *args) -class COMMethodInstance(object): - '''Binds a COM interface method.''' + +class COMMethodInstance: + """Binds a COM interface method.""" + def __init__(self, name, i, method): self.name = name self.i = i @@ -127,23 +146,29 @@ def __get__(self, obj, tp): if obj is not None: def _call(*args): - assert _debug_com('COM: IN {}({}, {})'.format(self.name, obj.__class__.__name__, args)) + assert _debug_com('COM: #{} IN {}({}, {})'.format(self.i, self.name, obj.__class__.__name__, args)) ret = self.method.get_field()(self.i, self.name)(obj, *args) - assert _debug_com('COM: OUT {}({}, {})'.format(self.name, obj.__class__.__name__, args)) + assert _debug_com('COM: #{} OUT {}({}, {})'.format(self.i, self.name, obj.__class__.__name__, args)) assert _debug_com('COM: RETURN {}'.format(ret)) return ret + return _call raise AttributeError() + class COMInterface(ctypes.Structure): - '''Dummy struct to serve as the type of all COM pointers.''' + """Dummy struct to serve as the type of all COM pointers.""" _fields_ = [ ('lpVtbl', ctypes.c_void_p), ] -class InterfaceMetaclass(type(ctypes.POINTER(COMInterface))): - '''Creates COM interface pointers.''' + +class InterfacePtrMeta(type(ctypes.POINTER(COMInterface))): + """Allows interfaces to be subclassed as ctypes POINTER and expects to be populated with data from a COM object. + TODO: Phase this out and properly use POINTER(Interface) where applicable. + """ + def __new__(cls, name, bases, dct): methods = [] for base in bases[::-1]: @@ -155,18 +180,189 @@ dct['_type_'] = COMInterface - return super(InterfaceMetaclass, cls).__new__(cls, name, bases, dct) + return super(InterfacePtrMeta, cls).__new__(cls, name, bases, dct) -# future.utils.with_metaclass does not work here, as the base class is from _ctypes.lib + +# pyglet.util.with_metaclass does not work here, as the base class is from _ctypes.lib # See https://wiki.python.org/moin/PortingToPy3k/BilingualQuickRef -Interface = InterfaceMetaclass(str('Interface'), (ctypes.POINTER(COMInterface),), { - '__doc__': 'Base COM interface pointer.', - }) +pInterface = InterfacePtrMeta(str('Interface'), + (ctypes.POINTER(COMInterface),), + {'__doc__': 'Base COM interface pointer.'}) + + +class COMInterfaceMeta(type): + """This differs in the original as an implemented interface object, not a POINTER object. + Used when the user must implement their own functions within an interface rather than + being created and generated by the COM object itself. The types are automatically inserted in the ctypes type + cache so it can recognize the type arguments. + """ + + def __new__(mcs, name, bases, dct): + methods = dct.pop("_methods_", None) + cls = type.__new__(mcs, name, bases, dct) + + if methods is not None: + cls._methods_ = methods + + if not bases: + _ptr_bases = (cls, COMPointer) + else: + _ptr_bases = (cls, ctypes.POINTER(bases[0])) + + # Class type is dynamically created inside __new__ based on metaclass inheritence; update ctypes cache manually. + from ctypes import _pointer_type_cache + _pointer_type_cache[cls] = type(COMPointer)("POINTER({})".format(cls.__name__), + _ptr_bases, + {"__interface__": cls}) + + return cls + + def __get_subclassed_methodcount(self): + """Returns the amount of COM methods in all subclasses to determine offset of methods. + Order must be exact from the source when calling COM methods. + """ + try: + result = 0 + for itf in self.mro()[1:-1]: + result += len(itf.__dict__["_methods_"]) + return result + except KeyError as err: + (name,) = err.args + if name == "_methods_": + raise TypeError("Interface '{}' requires a _methods_ attribute.".format(itf.__name__)) + raise + + +class COMPointerMeta(type(ctypes.c_void_p), COMInterfaceMeta): + """Required to prevent metaclass conflicts with inheritance.""" + + +class COMPointer(ctypes.c_void_p, metaclass=COMPointerMeta): + """COM Pointer base, could use c_void_p but need to override from_param .""" + + @classmethod + def from_param(cls, obj): + """Allows obj to return ctypes pointers, even if it's base is not a ctype. + In this case, all we simply want is a ctypes pointer matching the cls interface from the obj. + """ + if obj is None: + return + + try: + ptr_dct = obj._pointers + except AttributeError: + raise Exception("Interface method argument specified incorrectly, or passed wrong argument.", cls) + else: + try: + return ptr_dct[cls.__interface__] + except KeyError: + raise TypeError("Interface {} doesn't have a pointer in this class.".format(cls.__name__)) + + +def _missing_impl(interface_name, method_name): + """Functions that are not implemented use this to prevent errors when called.""" -class IUnknown(Interface): + def missing_cb_func(*args): + """Return E_NOTIMPL because the method is not implemented.""" + assert _debug_com("Undefined method: {0} was called in interface: {1}".format(method_name, interface_name)) + return 0 + + return missing_cb_func + + +def _found_impl(interface_name, method_name, method_func): + """If a method was found in class, we can set it as a callback.""" + + def cb_func(*args, **kw): + try: + result = method_func(*args, **kw) + except Exception as err: + raise err + + if not result: # QOL so callbacks don't need to specify a return for assumed OK's. + return 0 + + return result + + return cb_func + + +def _make_callback_func(interface, name, method_func): + """Create a callback function for ctypes if possible.""" + if method_func is None: + return _missing_impl(interface, name) + + return _found_impl(interface, name, method_func) + + +# Store structures with same fields to prevent duplicate table creations. +_cached_structures = {} + + +def create_vtbl_structure(fields, interface): + """Create virtual table structure with fields for use in COM's.""" + try: + return _cached_structures[fields] + except KeyError: + Vtbl = type("Vtbl_{}".format(interface.__name__), (ctypes.Structure,), {"_fields_": fields}) + _cached_structures[fields] = Vtbl + return Vtbl + + +class COMObject: + """A base class for defining a COM object for use with callbacks and custom implementations.""" + _interfaces_ = [] + + def __new__(cls, *args, **kw): + new_cls = super(COMObject, cls).__new__(cls) + assert len(cls._interfaces_) > 0, "Atleast one interface must be defined to use a COMObject." + new_cls._pointers = {} + new_cls.__create_interface_pointers() + return new_cls + + def __create_interface_pointers(cls): + """Create a custom ctypes structure to handle COM functions in a COM Object.""" + interfaces = tuple(cls._interfaces_) + for itf in interfaces[::-1]: + methods = [] + fields = [] + for interface in itf.__mro__[-2::-1]: + for method in interface._methods_: + name, com_method = method + + found_method = getattr(cls, name, None) + mth = _make_callback_func(itf.__name__, name, found_method) + + proto = ctypes.WINFUNCTYPE(com_method.restype, *com_method.argtypes) + + fields.append((name, proto)) + methods.append(proto(mth)) + + # Make a structure dynamically with the fields given. + itf_structure = create_vtbl_structure(tuple(fields), interface) + + # Assign the methods to the fields + vtbl = itf_structure(*methods) + + cls._pointers[itf] = ctypes.pointer(ctypes.pointer(vtbl)) + + +class Interface(metaclass=COMInterfaceMeta): + _methods_ = [] + + +class IUnknown(metaclass=COMInterfaceMeta): + """These methods are not implemented by default yet. Strictly for COM method ordering.""" _methods_ = [ ('QueryInterface', STDMETHOD(REFIID, ctypes.c_void_p)), ('AddRef', METHOD(ctypes.c_int)), ('Release', METHOD(ctypes.c_int)) ] + +class pIUnknown(pInterface): + _methods_ = [ + ('QueryInterface', STDMETHOD(REFIID, ctypes.c_void_p)), + ('AddRef', METHOD(ctypes.c_int)), + ('Release', METHOD(ctypes.c_int)) + ] diff -Nru pyglet-1.4.10/pyglet/debug.py pyglet-1.5.14/pyglet/debug.py --- pyglet-1.4.10/pyglet/debug.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/debug.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -# ---------------------------------------------------------------------------- -# pyglet -# Copyright (c) 2006-2008 Alex Holkner -# Copyright (c) 2008-2020 pyglet contributors -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in -# the documentation and/or other materials provided with the -# distribution. -# * Neither the name of pyglet nor the names of its -# contributors may be used to endorse or promote products -# derived from this software without specific prior written -# permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -# POSSIBILITY OF SUCH DAMAGE. -# ---------------------------------------------------------------------------- -from __future__ import print_function - -import pyglet - - -def debug_print(enabled_or_option='debug'): - """Get a debug printer that is enabled based on a boolean input or a pyglet option. - The debug print function returned should be used in an assert. This way it can be - optimized out when running python with the -O flag. - - Usage example:: - - from pyglet.debug import debug_print - _debug_media = debug_print('debug_media') - - def some_func(): - assert _debug_media('My debug statement') - - :parameters: - `enabled_or_options` : bool or str - If a bool is passed, debug printing is enabled if it is True. If str is passed - debug printing is enabled if the pyglet option with that name is True. - - :returns: Function for debug printing. - """ - if isinstance(enabled_or_option, bool): - enabled = enabled_or_option - else: - enabled = pyglet.options.get(enabled_or_option, False) - - if enabled: - def _debug_print(*args, **kwargs): - print(*args, **kwargs) - return True - - else: - def _debug_print(*args, **kwargs): - return True - - return _debug_print - diff -Nru pyglet-1.4.10/pyglet/event.py pyglet-1.5.14/pyglet/event.py --- pyglet-1.4.10/pyglet/event.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/event.py 2020-12-23 15:47:40.000000000 +0000 @@ -151,20 +151,12 @@ dispatcher.push_handlers(my_handler_instance) """ -from builtins import object -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -import sys import inspect + from functools import partial -import sys -from .compat import WeakMethod +from weakref import WeakMethod -# PYTHON2 - remove this legacy backwards compatibility hack: -if sys.version_info < (3, 2): - inspect.getfullargspec = inspect.getargspec EVENT_HANDLED = True EVENT_UNHANDLED = None @@ -176,7 +168,7 @@ pass -class EventDispatcher(object): +class EventDispatcher: """Generic event dispatcher interface. See the module docstring for usage. @@ -422,8 +414,10 @@ try: if getattr(self, event_type)(*args): return EVENT_HANDLED - except AttributeError: - pass + except AttributeError as e: + event_op = getattr(self, event_type, None) + if callable(event_op): + raise e except TypeError as exception: self._raise_dispatch_exception(event_type, args, getattr(self, event_type), exception) else: diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/builtins/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/builtins/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/builtins/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/builtins/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from __builtin__ import * - # Overwrite any old definitions with the equivalent future.builtins ones: - from future.builtins import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/copyreg/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/copyreg/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/copyreg/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/copyreg/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from copy_reg import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/_dummy_thread/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/_dummy_thread/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/_dummy_thread/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/_dummy_thread/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from dummy_thread import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/html/entities.py pyglet-1.5.14/pyglet/extlibs/future/py2/html/entities.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/html/entities.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/html/entities.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from html.entities import * -else: - from future.moves.html.entities import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/html/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/html/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/html/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/html/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from future.moves.html import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/html/parser.py pyglet-1.5.14/pyglet/extlibs/future/py2/html/parser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/html/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/html/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] == 3: - raise ImportError('Cannot import module from python-future source folder') -else: - from future.moves.html.parser import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/http/client.py pyglet-1.5.14/pyglet/extlibs/future/py2/http/client.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/http/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/http/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from httplib import * -from httplib import HTTPMessage - -# These constants aren't included in __all__ in httplib.py: - -from httplib import (HTTP_PORT, - HTTPS_PORT, - - _CS_IDLE, - _CS_REQ_STARTED, - _CS_REQ_SENT, - - CONTINUE, - SWITCHING_PROTOCOLS, - PROCESSING, - - OK, - CREATED, - ACCEPTED, - NON_AUTHORITATIVE_INFORMATION, - NO_CONTENT, - RESET_CONTENT, - PARTIAL_CONTENT, - MULTI_STATUS, - IM_USED, - - MULTIPLE_CHOICES, - MOVED_PERMANENTLY, - FOUND, - SEE_OTHER, - NOT_MODIFIED, - USE_PROXY, - TEMPORARY_REDIRECT, - - BAD_REQUEST, - UNAUTHORIZED, - PAYMENT_REQUIRED, - FORBIDDEN, - NOT_FOUND, - METHOD_NOT_ALLOWED, - NOT_ACCEPTABLE, - PROXY_AUTHENTICATION_REQUIRED, - REQUEST_TIMEOUT, - CONFLICT, - GONE, - LENGTH_REQUIRED, - PRECONDITION_FAILED, - REQUEST_ENTITY_TOO_LARGE, - REQUEST_URI_TOO_LONG, - UNSUPPORTED_MEDIA_TYPE, - REQUESTED_RANGE_NOT_SATISFIABLE, - EXPECTATION_FAILED, - UNPROCESSABLE_ENTITY, - LOCKED, - FAILED_DEPENDENCY, - UPGRADE_REQUIRED, - - INTERNAL_SERVER_ERROR, - NOT_IMPLEMENTED, - BAD_GATEWAY, - SERVICE_UNAVAILABLE, - GATEWAY_TIMEOUT, - HTTP_VERSION_NOT_SUPPORTED, - INSUFFICIENT_STORAGE, - NOT_EXTENDED, - - MAXAMOUNT, - ) - -# These are not available on Python 2.6.x: -try: - from httplib import LineTooLong, LineAndFileWrapper -except ImportError: - pass - -# These may not be available on all versions of Python 2.6.x or 2.7.x -try: - from httplib import ( - _MAXLINE, - _MAXHEADERS, - _is_legal_header_name, - _is_illegal_header_value, - _METHODS_EXPECTING_BODY - ) -except ImportError: - pass diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/http/cookiejar.py pyglet-1.5.14/pyglet/extlibs/future/py2/http/cookiejar.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/http/cookiejar.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/http/cookiejar.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from cookielib import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/http/cookies.py pyglet-1.5.14/pyglet/extlibs/future/py2/http/cookies.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/http/cookies.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/http/cookies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from Cookie import * -from Cookie import Morsel # left out of __all__ on Py2.7! diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/http/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/http/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/http/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/http/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - pass -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/http/server.py pyglet-1.5.14/pyglet/extlibs/future/py2/http/server.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/http/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/http/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from BaseHTTPServer import * -from CGIHTTPServer import * -from SimpleHTTPServer import * -try: - from CGIHTTPServer import _url_collapse_path # needed for a test -except ImportError: - try: - # Python 2.7.0 to 2.7.3 - from CGIHTTPServer import ( - _url_collapse_path_split as _url_collapse_path) - except ImportError: - # Doesn't exist on Python 2.6.x. Ignore it. - pass diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/_markupbase/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/_markupbase/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/_markupbase/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/_markupbase/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from markupbase import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/queue/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/queue/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/queue/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/queue/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from Queue import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/reprlib/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/reprlib/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/reprlib/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/reprlib/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from repr import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/socketserver/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/socketserver/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/socketserver/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/socketserver/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from SocketServer import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/_thread/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/_thread/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/_thread/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/_thread/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from thread import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/colorchooser.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/colorchooser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/colorchooser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/colorchooser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.colorchooser import * -else: - try: - from tkColorChooser import * - except ImportError: - raise ImportError('The tkColorChooser module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/commondialog.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/commondialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/commondialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/commondialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.commondialog import * -else: - try: - from tkCommonDialog import * - except ImportError: - raise ImportError('The tkCommonDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/constants.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/constants.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/constants.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/constants.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.constants import * -else: - try: - from Tkconstants import * - except ImportError: - raise ImportError('The Tkconstants module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/dialog.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/dialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/dialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/dialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dialog import * -else: - try: - from Dialog import * - except ImportError: - raise ImportError('The Dialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/dnd.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/dnd.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/dnd.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/dnd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dnd import * -else: - try: - from Tkdnd import * - except ImportError: - raise ImportError('The Tkdnd module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/filedialog.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/filedialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/filedialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/filedialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.filedialog import * -else: - try: - from FileDialog import * - except ImportError: - raise ImportError('The FileDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - try: - from tkFileDialog import * - except ImportError: - raise ImportError('The tkFileDialog module is missing. Does your Py2 ' - 'installation include tkinter?') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/font.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/font.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/font.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/font.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.font import * -else: - try: - from tkFont import * - except ImportError: - raise ImportError('The tkFont module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from Tkinter import * - from Tkinter import (_cnfmerge, _default_root, _flatten, _join, _setit, - _splitdict, _stringify, _support_default_root, _test, - _tkinter) -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/messagebox.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/messagebox.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/messagebox.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/messagebox.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.messagebox import * -else: - try: - from tkMessageBox import * - except ImportError: - raise ImportError('The tkMessageBox module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/scrolledtext.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/scrolledtext.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/scrolledtext.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/scrolledtext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.scrolledtext import * -else: - try: - from ScrolledText import * - except ImportError: - raise ImportError('The ScrolledText module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/simpledialog.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/simpledialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/simpledialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/simpledialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.simpledialog import * -else: - try: - from SimpleDialog import * - except ImportError: - raise ImportError('The SimpleDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/tix.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/tix.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/tix.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/tix.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.tix import * -else: - try: - from Tix import * - except ImportError: - raise ImportError('The Tix module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/ttk.py pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/ttk.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/tkinter/ttk.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/tkinter/ttk.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.ttk import * -else: - try: - from ttk import * - except ImportError: - raise ImportError('The ttk module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/winreg/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/winreg/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/winreg/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/winreg/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from _winreg import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/xmlrpc/client.py pyglet-1.5.14/pyglet/extlibs/future/py2/xmlrpc/client.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/xmlrpc/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/xmlrpc/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 -from xmlrpclib import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/xmlrpc/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2/xmlrpc/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/xmlrpc/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/xmlrpc/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - pass -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2/xmlrpc/server.py pyglet-1.5.14/pyglet/extlibs/future/py2/xmlrpc/server.py --- pyglet-1.4.10/pyglet/extlibs/future/py2/xmlrpc/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2/xmlrpc/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 -from xmlrpclib import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/datetime.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/datetime.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/datetime.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/datetime.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2152 +0,0 @@ -"""Concrete date/time and related types. - -See http://www.iana.org/time-zones/repository/tz-link.html for -time zone and DST data sources. -""" -from __future__ import division -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import absolute_import -from future.builtins import str -from future.builtins import bytes -from future.builtins import map -from future.builtins import round -from future.builtins import int -from future.builtins import object -from future.utils import native_str, PY2 - -import time as _time -import math as _math - -def _cmp(x, y): - return 0 if x == y else 1 if x > y else -1 - -MINYEAR = 1 -MAXYEAR = 9999 -_MAXORDINAL = 3652059 # date.max.toordinal() - -# Utility functions, adapted from Python's Demo/classes/Dates.py, which -# also assumes the current Gregorian calendar indefinitely extended in -# both directions. Difference: Dates.py calls January 1 of year 0 day -# number 1. The code here calls January 1 of year 1 day number 1. This is -# to match the definition of the "proleptic Gregorian" calendar in Dershowitz -# and Reingold's "Calendrical Calculations", where it's the base calendar -# for all computations. See the book for algorithms for converting between -# proleptic Gregorian ordinals and many other calendar systems. - -_DAYS_IN_MONTH = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - -_DAYS_BEFORE_MONTH = [None] -dbm = 0 -for dim in _DAYS_IN_MONTH[1:]: - _DAYS_BEFORE_MONTH.append(dbm) - dbm += dim -del dbm, dim - -def _is_leap(year): - "year -> 1 if leap year, else 0." - return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) - -def _days_before_year(year): - "year -> number of days before January 1st of year." - y = year - 1 - return y*365 + y//4 - y//100 + y//400 - -def _days_in_month(year, month): - "year, month -> number of days in that month in that year." - assert 1 <= month <= 12, month - if month == 2 and _is_leap(year): - return 29 - return _DAYS_IN_MONTH[month] - -def _days_before_month(year, month): - "year, month -> number of days in year preceding first day of month." - assert 1 <= month <= 12, 'month must be in 1..12' - return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year)) - -def _ymd2ord(year, month, day): - "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." - assert 1 <= month <= 12, 'month must be in 1..12' - dim = _days_in_month(year, month) - assert 1 <= day <= dim, ('day must be in 1..%d' % dim) - return (_days_before_year(year) + - _days_before_month(year, month) + - day) - -_DI400Y = _days_before_year(401) # number of days in 400 years -_DI100Y = _days_before_year(101) # " " " " 100 " -_DI4Y = _days_before_year(5) # " " " " 4 " - -# A 4-year cycle has an extra leap day over what we'd get from pasting -# together 4 single years. -assert _DI4Y == 4 * 365 + 1 - -# Similarly, a 400-year cycle has an extra leap day over what we'd get from -# pasting together 4 100-year cycles. -assert _DI400Y == 4 * _DI100Y + 1 - -# OTOH, a 100-year cycle has one fewer leap day than we'd get from -# pasting together 25 4-year cycles. -assert _DI100Y == 25 * _DI4Y - 1 - -def _ord2ymd(n): - "ordinal -> (year, month, day), considering 01-Jan-0001 as day 1." - - # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years - # repeats exactly every 400 years. The basic strategy is to find the - # closest 400-year boundary at or before n, then work with the offset - # from that boundary to n. Life is much clearer if we subtract 1 from - # n first -- then the values of n at 400-year boundaries are exactly - # those divisible by _DI400Y: - # - # D M Y n n-1 - # -- --- ---- ---------- ---------------- - # 31 Dec -400 -_DI400Y -_DI400Y -1 - # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary - # ... - # 30 Dec 000 -1 -2 - # 31 Dec 000 0 -1 - # 1 Jan 001 1 0 400-year boundary - # 2 Jan 001 2 1 - # 3 Jan 001 3 2 - # ... - # 31 Dec 400 _DI400Y _DI400Y -1 - # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary - n -= 1 - n400, n = divmod(n, _DI400Y) - year = n400 * 400 + 1 # ..., -399, 1, 401, ... - - # Now n is the (non-negative) offset, in days, from January 1 of year, to - # the desired date. Now compute how many 100-year cycles precede n. - # Note that it's possible for n100 to equal 4! In that case 4 full - # 100-year cycles precede the desired day, which implies the desired - # day is December 31 at the end of a 400-year cycle. - n100, n = divmod(n, _DI100Y) - - # Now compute how many 4-year cycles precede it. - n4, n = divmod(n, _DI4Y) - - # And now how many single years. Again n1 can be 4, and again meaning - # that the desired day is December 31 at the end of the 4-year cycle. - n1, n = divmod(n, 365) - - year += n100 * 100 + n4 * 4 + n1 - if n1 == 4 or n100 == 4: - assert n == 0 - return year-1, 12, 31 - - # Now the year is correct, and n is the offset from January 1. We find - # the month via an estimate that's either exact or one too large. - leapyear = n1 == 3 and (n4 != 24 or n100 == 3) - assert leapyear == _is_leap(year) - month = (n + 50) >> 5 - preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear) - if preceding > n: # estimate is too large - month -= 1 - preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear) - n -= preceding - assert 0 <= n < _days_in_month(year, month) - - # Now the year and month are correct, and n is the offset from the - # start of that month: we're done! - return year, month, n+1 - -# Month and day names. For localized versions, see the calendar module. -_MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -_DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] - - -def _build_struct_time(y, m, d, hh, mm, ss, dstflag): - wday = (_ymd2ord(y, m, d) + 6) % 7 - dnum = _days_before_month(y, m) + d - return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag)) - -def _format_time(hh, mm, ss, us): - # Skip trailing microseconds when us==0. - result = "%02d:%02d:%02d" % (hh, mm, ss) - if us: - result += ".%06d" % us - return result - -# Correctly substitute for %z and %Z escapes in strftime formats. -def _wrap_strftime(object, format, timetuple): - # Don't call utcoffset() or tzname() unless actually needed. - freplace = None # the string to use for %f - zreplace = None # the string to use for %z - Zreplace = None # the string to use for %Z - - # Scan format for %z and %Z escapes, replacing as needed. - newformat = [] - push = newformat.append - i, n = 0, len(format) - while i < n: - ch = format[i] - i += 1 - if ch == '%': - if i < n: - ch = format[i] - i += 1 - if ch == 'f': - if freplace is None: - freplace = '%06d' % getattr(object, - 'microsecond', 0) - newformat.append(freplace) - elif ch == 'z': - if zreplace is None: - zreplace = "" - if hasattr(object, "utcoffset"): - offset = object.utcoffset() - if offset is not None: - sign = '+' - if offset.days < 0: - offset = -offset - sign = '-' - h, m = divmod(offset, timedelta(hours=1)) - assert not m % timedelta(minutes=1), "whole minute" - m //= timedelta(minutes=1) - zreplace = '%c%02d%02d' % (sign, h, m) - assert '%' not in zreplace - newformat.append(zreplace) - elif ch == 'Z': - if Zreplace is None: - Zreplace = "" - if hasattr(object, "tzname"): - s = object.tzname() - if s is not None: - # strftime is going to have at this: escape % - Zreplace = s.replace('%', '%%') - newformat.append(Zreplace) - else: - push('%') - push(ch) - else: - push('%') - else: - push(ch) - newformat = "".join(newformat) - return _time.strftime(newformat, timetuple) - -def _call_tzinfo_method(tzinfo, methname, tzinfoarg): - if tzinfo is None: - return None - return getattr(tzinfo, methname)(tzinfoarg) - -# Just raise TypeError if the arg isn't None or a string. -def _check_tzname(name): - if name is not None and not isinstance(name, str): - raise TypeError("tzinfo.tzname() must return None or string, " - "not '%s'" % type(name)) - -# name is the offset-producing method, "utcoffset" or "dst". -# offset is what it returned. -# If offset isn't None or timedelta, raises TypeError. -# If offset is None, returns None. -# Else offset is checked for being in range, and a whole # of minutes. -# If it is, its integer value is returned. Else ValueError is raised. -def _check_utc_offset(name, offset): - assert name in ("utcoffset", "dst") - if offset is None: - return - if not isinstance(offset, timedelta): - raise TypeError("tzinfo.%s() must return None " - "or timedelta, not '%s'" % (name, type(offset))) - if offset % timedelta(minutes=1) or offset.microseconds: - raise ValueError("tzinfo.%s() must return a whole number " - "of minutes, got %s" % (name, offset)) - if not -timedelta(1) < offset < timedelta(1): - raise ValueError("%s()=%s, must be must be strictly between" - " -timedelta(hours=24) and timedelta(hours=24)" - % (name, offset)) - -def _check_date_fields(year, month, day): - if not isinstance(year, int): - raise TypeError('int expected') - if not MINYEAR <= year <= MAXYEAR: - raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year) - if not 1 <= month <= 12: - raise ValueError('month must be in 1..12', month) - dim = _days_in_month(year, month) - if not 1 <= day <= dim: - raise ValueError('day must be in 1..%d' % dim, day) - -def _check_time_fields(hour, minute, second, microsecond): - if not isinstance(hour, int): - raise TypeError('int expected') - if not 0 <= hour <= 23: - raise ValueError('hour must be in 0..23', hour) - if not 0 <= minute <= 59: - raise ValueError('minute must be in 0..59', minute) - if not 0 <= second <= 59: - raise ValueError('second must be in 0..59', second) - if not 0 <= microsecond <= 999999: - raise ValueError('microsecond must be in 0..999999', microsecond) - -def _check_tzinfo_arg(tz): - if tz is not None and not isinstance(tz, tzinfo): - raise TypeError("tzinfo argument must be None or of a tzinfo subclass") - -def _cmperror(x, y): - raise TypeError("can't compare '%s' to '%s'" % ( - type(x).__name__, type(y).__name__)) - -class timedelta(object): - """Represent the difference between two datetime objects. - - Supported operators: - - - add, subtract timedelta - - unary plus, minus, abs - - compare to timedelta - - multiply, divide by int - - In addition, datetime supports subtraction of two datetime objects - returning a timedelta, and addition or subtraction of a datetime - and a timedelta giving a datetime. - - Representation: (days, seconds, microseconds). Why? Because I - felt like it. - """ - __slots__ = '_days', '_seconds', '_microseconds' - - def __new__(cls, days=0, seconds=0, microseconds=0, - milliseconds=0, minutes=0, hours=0, weeks=0): - # Doing this efficiently and accurately in C is going to be difficult - # and error-prone, due to ubiquitous overflow possibilities, and that - # C double doesn't have enough bits of precision to represent - # microseconds over 10K years faithfully. The code here tries to make - # explicit where go-fast assumptions can be relied on, in order to - # guide the C implementation; it's way more convoluted than speed- - # ignoring auto-overflow-to-long idiomatic Python could be. - - # XXX Check that all inputs are ints or floats. - - # Final values, all integer. - # s and us fit in 32-bit signed ints; d isn't bounded. - d = s = us = 0 - - # Normalize everything to days, seconds, microseconds. - days += weeks*7 - seconds += minutes*60 + hours*3600 - microseconds += milliseconds*1000 - - # Get rid of all fractions, and normalize s and us. - # Take a deep breath . - if isinstance(days, float): - dayfrac, days = _math.modf(days) - daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.)) - assert daysecondswhole == int(daysecondswhole) # can't overflow - s = int(daysecondswhole) - assert days == int(days) - d = int(days) - else: - daysecondsfrac = 0.0 - d = days - assert isinstance(daysecondsfrac, float) - assert abs(daysecondsfrac) <= 1.0 - assert isinstance(d, int) - assert abs(s) <= 24 * 3600 - # days isn't referenced again before redefinition - - if isinstance(seconds, float): - secondsfrac, seconds = _math.modf(seconds) - assert seconds == int(seconds) - seconds = int(seconds) - secondsfrac += daysecondsfrac - assert abs(secondsfrac) <= 2.0 - else: - secondsfrac = daysecondsfrac - # daysecondsfrac isn't referenced again - assert isinstance(secondsfrac, float) - assert abs(secondsfrac) <= 2.0 - - assert isinstance(seconds, int) - days, seconds = divmod(seconds, 24*3600) - d += days - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 2 * 24 * 3600 - # seconds isn't referenced again before redefinition - - usdouble = secondsfrac * 1e6 - assert abs(usdouble) < 2.1e6 # exact value not critical - # secondsfrac isn't referenced again - - if isinstance(microseconds, float): - microseconds += usdouble - microseconds = round(microseconds, 0) - seconds, microseconds = divmod(microseconds, 1e6) - assert microseconds == int(microseconds) - assert seconds == int(seconds) - days, seconds = divmod(seconds, 24.*3600.) - assert days == int(days) - assert seconds == int(seconds) - d += int(days) - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 3 * 24 * 3600 - else: - seconds, microseconds = divmod(microseconds, 1000000) - days, seconds = divmod(seconds, 24*3600) - d += days - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 3 * 24 * 3600 - microseconds = float(microseconds) - microseconds += usdouble - microseconds = round(microseconds, 0) - assert abs(s) <= 3 * 24 * 3600 - assert abs(microseconds) < 3.1e6 - - # Just a little bit of carrying possible for microseconds and seconds. - assert isinstance(microseconds, float) - assert int(microseconds) == microseconds - us = int(microseconds) - seconds, us = divmod(us, 1000000) - s += seconds # cant't overflow - assert isinstance(s, int) - days, s = divmod(s, 24*3600) - d += days - - assert isinstance(d, int) - assert isinstance(s, int) and 0 <= s < 24*3600 - assert isinstance(us, int) and 0 <= us < 1000000 - - self = object.__new__(cls) - - self._days = d - self._seconds = s - self._microseconds = us - if abs(d) > 999999999: - raise OverflowError("timedelta # of days is too large: %d" % d) - - return self - - def __repr__(self): - if self._microseconds: - return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__, - self._days, - self._seconds, - self._microseconds) - if self._seconds: - return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__, - self._days, - self._seconds) - return "%s(%d)" % ('datetime.' + self.__class__.__name__, self._days) - - def __str__(self): - mm, ss = divmod(self._seconds, 60) - hh, mm = divmod(mm, 60) - s = "%d:%02d:%02d" % (hh, mm, ss) - if self._days: - def plural(n): - return n, abs(n) != 1 and "s" or "" - s = ("%d day%s, " % plural(self._days)) + s - if self._microseconds: - s = s + ".%06d" % self._microseconds - return s - - def total_seconds(self): - """Total seconds in the duration.""" - return ((self.days * 86400 + self.seconds)*10**6 + - self.microseconds) / 10**6 - - # Read-only field accessors - @property - def days(self): - """days""" - return self._days - - @property - def seconds(self): - """seconds""" - return self._seconds - - @property - def microseconds(self): - """microseconds""" - return self._microseconds - - def __add__(self, other): - if isinstance(other, timedelta): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(self._days + other._days, - self._seconds + other._seconds, - self._microseconds + other._microseconds) - return NotImplemented - - __radd__ = __add__ - - def __sub__(self, other): - if isinstance(other, timedelta): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(self._days - other._days, - self._seconds - other._seconds, - self._microseconds - other._microseconds) - return NotImplemented - - def __rsub__(self, other): - if isinstance(other, timedelta): - return -self + other - return NotImplemented - - def __neg__(self): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(-self._days, - -self._seconds, - -self._microseconds) - - def __pos__(self): - return self - - def __abs__(self): - if self._days < 0: - return -self - else: - return self - - def __mul__(self, other): - if isinstance(other, int): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(self._days * other, - self._seconds * other, - self._microseconds * other) - if isinstance(other, float): - a, b = other.as_integer_ratio() - return self * a / b - return NotImplemented - - __rmul__ = __mul__ - - def _to_microseconds(self): - return ((self._days * (24*3600) + self._seconds) * 1000000 + - self._microseconds) - - def __floordiv__(self, other): - if not isinstance(other, (int, timedelta)): - return NotImplemented - usec = self._to_microseconds() - if isinstance(other, timedelta): - return usec // other._to_microseconds() - if isinstance(other, int): - return timedelta(0, 0, usec // other) - - def __truediv__(self, other): - if not isinstance(other, (int, float, timedelta)): - return NotImplemented - usec = self._to_microseconds() - if isinstance(other, timedelta): - return usec / other._to_microseconds() - if isinstance(other, int): - return timedelta(0, 0, usec / other) - if isinstance(other, float): - a, b = other.as_integer_ratio() - return timedelta(0, 0, b * usec / a) - - def __mod__(self, other): - if isinstance(other, timedelta): - r = self._to_microseconds() % other._to_microseconds() - return timedelta(0, 0, r) - return NotImplemented - - def __divmod__(self, other): - if isinstance(other, timedelta): - q, r = divmod(self._to_microseconds(), - other._to_microseconds()) - return q, timedelta(0, 0, r) - return NotImplemented - - # Comparisons of timedelta objects with other. - - def __eq__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) == 0 - else: - return False - - def __ne__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) != 0 - else: - return True - - def __le__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) <= 0 - else: - _cmperror(self, other) - - def __lt__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) < 0 - else: - _cmperror(self, other) - - def __ge__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) >= 0 - else: - _cmperror(self, other) - - def __gt__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) > 0 - else: - _cmperror(self, other) - - def _cmp(self, other): - assert isinstance(other, timedelta) - return _cmp(self._getstate(), other._getstate()) - - def __hash__(self): - return hash(self._getstate()) - - def __bool__(self): - return (self._days != 0 or - self._seconds != 0 or - self._microseconds != 0) - - # Pickle support. - - def _getstate(self): - return (self._days, self._seconds, self._microseconds) - - def __reduce__(self): - return (self.__class__, self._getstate()) - -timedelta.min = timedelta(-999999999) -timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59, - microseconds=999999) -timedelta.resolution = timedelta(microseconds=1) - -class date(object): - """Concrete date type. - - Constructors: - - __new__() - fromtimestamp() - today() - fromordinal() - - Operators: - - __repr__, __str__ - __cmp__, __hash__ - __add__, __radd__, __sub__ (add/radd only with timedelta arg) - - Methods: - - timetuple() - toordinal() - weekday() - isoweekday(), isocalendar(), isoformat() - ctime() - strftime() - - Properties (readonly): - year, month, day - """ - __slots__ = '_year', '_month', '_day' - - def __new__(cls, year, month=None, day=None): - """Constructor. - - Arguments: - - year, month, day (required, base 1) - """ - if (isinstance(year, bytes) and len(year) == 4 and - 1 <= year[2] <= 12 and month is None): # Month is sane - # Pickle support - self = object.__new__(cls) - self.__setstate(year) - return self - _check_date_fields(year, month, day) - self = object.__new__(cls) - self._year = year - self._month = month - self._day = day - return self - - # Additional constructors - - @classmethod - def fromtimestamp(cls, t): - "Construct a date from a POSIX timestamp (like time.time())." - y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t) - return cls(y, m, d) - - @classmethod - def today(cls): - "Construct a date from time.time()." - t = _time.time() - return cls.fromtimestamp(t) - - @classmethod - def fromordinal(cls, n): - """Contruct a date from a proleptic Gregorian ordinal. - - January 1 of year 1 is day 1. Only the year, month and day are - non-zero in the result. - """ - y, m, d = _ord2ymd(n) - return cls(y, m, d) - - # Conversions to string - - def __repr__(self): - """Convert to formal string, for repr(). - - >>> dt = datetime(2010, 1, 1) - >>> repr(dt) - 'datetime.datetime(2010, 1, 1, 0, 0)' - - >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc) - >>> repr(dt) - 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)' - """ - return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__, - self._year, - self._month, - self._day) - # XXX These shouldn't depend on time.localtime(), because that - # clips the usable dates to [1970 .. 2038). At least ctime() is - # easily done without using strftime() -- that's better too because - # strftime("%c", ...) is locale specific. - - - def ctime(self): - "Return ctime() style string." - weekday = self.toordinal() % 7 or 7 - return "%s %s %2d 00:00:00 %04d" % ( - _DAYNAMES[weekday], - _MONTHNAMES[self._month], - self._day, self._year) - - def strftime(self, fmt): - "Format using strftime()." - return _wrap_strftime(self, fmt, self.timetuple()) - - def __format__(self, fmt): - if len(fmt) != 0: - return self.strftime(fmt) - return str(self) - - def isoformat(self): - """Return the date formatted according to ISO. - - This is 'YYYY-MM-DD'. - - References: - - http://www.w3.org/TR/NOTE-datetime - - http://www.cl.cam.ac.uk/~mgk25/iso-time.html - """ - return "%04d-%02d-%02d" % (self._year, self._month, self._day) - - __str__ = isoformat - - # Read-only field accessors - @property - def year(self): - """year (1-9999)""" - return self._year - - @property - def month(self): - """month (1-12)""" - return self._month - - @property - def day(self): - """day (1-31)""" - return self._day - - # Standard conversions, __cmp__, __hash__ (and helpers) - - def timetuple(self): - "Return local time tuple compatible with time.localtime()." - return _build_struct_time(self._year, self._month, self._day, - 0, 0, 0, -1) - - def toordinal(self): - """Return proleptic Gregorian ordinal for the year, month and day. - - January 1 of year 1 is day 1. Only the year, month and day values - contribute to the result. - """ - return _ymd2ord(self._year, self._month, self._day) - - def replace(self, year=None, month=None, day=None): - """Return a new date with new values for the specified fields.""" - if year is None: - year = self._year - if month is None: - month = self._month - if day is None: - day = self._day - _check_date_fields(year, month, day) - return date(year, month, day) - - # Comparisons of date objects with other. - - def __eq__(self, other): - if isinstance(other, date): - return self._cmp(other) == 0 - return NotImplemented - - def __ne__(self, other): - if isinstance(other, date): - return self._cmp(other) != 0 - return NotImplemented - - def __le__(self, other): - if isinstance(other, date): - return self._cmp(other) <= 0 - return NotImplemented - - def __lt__(self, other): - if isinstance(other, date): - return self._cmp(other) < 0 - return NotImplemented - - def __ge__(self, other): - if isinstance(other, date): - return self._cmp(other) >= 0 - return NotImplemented - - def __gt__(self, other): - if isinstance(other, date): - return self._cmp(other) > 0 - return NotImplemented - - def _cmp(self, other): - assert isinstance(other, date) - y, m, d = self._year, self._month, self._day - y2, m2, d2 = other._year, other._month, other._day - return _cmp((y, m, d), (y2, m2, d2)) - - def __hash__(self): - "Hash." - return hash(self._getstate()) - - # Computations - - def __add__(self, other): - "Add a date to a timedelta." - if isinstance(other, timedelta): - o = self.toordinal() + other.days - if 0 < o <= _MAXORDINAL: - return date.fromordinal(o) - raise OverflowError("result out of range") - return NotImplemented - - __radd__ = __add__ - - def __sub__(self, other): - """Subtract two dates, or a date and a timedelta.""" - if isinstance(other, timedelta): - return self + timedelta(-other.days) - if isinstance(other, date): - days1 = self.toordinal() - days2 = other.toordinal() - return timedelta(days1 - days2) - return NotImplemented - - def weekday(self): - "Return day of the week, where Monday == 0 ... Sunday == 6." - return (self.toordinal() + 6) % 7 - - # Day-of-the-week and week-of-the-year, according to ISO - - def isoweekday(self): - "Return day of the week, where Monday == 1 ... Sunday == 7." - # 1-Jan-0001 is a Monday - return self.toordinal() % 7 or 7 - - def isocalendar(self): - """Return a 3-tuple containing ISO year, week number, and weekday. - - The first ISO week of the year is the (Mon-Sun) week - containing the year's first Thursday; everything else derives - from that. - - The first week is 1; Monday is 1 ... Sunday is 7. - - ISO calendar algorithm taken from - http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm - """ - year = self._year - week1monday = _isoweek1monday(year) - today = _ymd2ord(self._year, self._month, self._day) - # Internally, week and day have origin 0 - week, day = divmod(today - week1monday, 7) - if week < 0: - year -= 1 - week1monday = _isoweek1monday(year) - week, day = divmod(today - week1monday, 7) - elif week >= 52: - if today >= _isoweek1monday(year+1): - year += 1 - week = 0 - return year, week+1, day+1 - - # Pickle support. - - def _getstate(self): - yhi, ylo = divmod(self._year, 256) - return bytes([yhi, ylo, self._month, self._day]), - - def __setstate(self, string): - if len(string) != 4 or not (1 <= string[2] <= 12): - raise TypeError("not enough arguments") - yhi, ylo, self._month, self._day = string - self._year = yhi * 256 + ylo - - def __reduce__(self): - return (self.__class__, self._getstate()) - -_date_class = date # so functions w/ args named "date" can get at the class - -date.min = date(1, 1, 1) -date.max = date(9999, 12, 31) -date.resolution = timedelta(days=1) - -class tzinfo(object): - """Abstract base class for time zone info classes. - - Subclasses must override the name(), utcoffset() and dst() methods. - """ - __slots__ = () - def tzname(self, dt): - "datetime -> string name of time zone." - raise NotImplementedError("tzinfo subclass must override tzname()") - - def utcoffset(self, dt): - "datetime -> minutes east of UTC (negative for west of UTC)" - raise NotImplementedError("tzinfo subclass must override utcoffset()") - - def dst(self, dt): - """datetime -> DST offset in minutes east of UTC. - - Return 0 if DST not in effect. utcoffset() must include the DST - offset. - """ - raise NotImplementedError("tzinfo subclass must override dst()") - - def fromutc(self, dt): - "datetime in UTC -> datetime in local time." - - if not isinstance(dt, datetime): - raise TypeError("fromutc() requires a datetime argument") - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - dtoff = dt.utcoffset() - if dtoff is None: - raise ValueError("fromutc() requires a non-None utcoffset() " - "result") - - # See the long comment block at the end of this file for an - # explanation of this algorithm. - dtdst = dt.dst() - if dtdst is None: - raise ValueError("fromutc() requires a non-None dst() result") - delta = dtoff - dtdst - if delta: - dt += delta - dtdst = dt.dst() - if dtdst is None: - raise ValueError("fromutc(): dt.dst gave inconsistent " - "results; cannot convert") - return dt + dtdst - - # Pickle support. - - def __reduce__(self): - getinitargs = getattr(self, "__getinitargs__", None) - if getinitargs: - args = getinitargs() - else: - args = () - getstate = getattr(self, "__getstate__", None) - if getstate: - state = getstate() - else: - state = getattr(self, "__dict__", None) or None - if state is None: - return (self.__class__, args) - else: - return (self.__class__, args, state) - -_tzinfo_class = tzinfo - -class time(object): - """Time with time zone. - - Constructors: - - __new__() - - Operators: - - __repr__, __str__ - __cmp__, __hash__ - - Methods: - - strftime() - isoformat() - utcoffset() - tzname() - dst() - - Properties (readonly): - hour, minute, second, microsecond, tzinfo - """ - - def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None): - """Constructor. - - Arguments: - - hour, minute (required) - second, microsecond (default to zero) - tzinfo (default to None) - """ - self = object.__new__(cls) - if isinstance(hour, bytes) and len(hour) == 6: - # Pickle support - self.__setstate(hour, minute or None) - return self - _check_tzinfo_arg(tzinfo) - _check_time_fields(hour, minute, second, microsecond) - self._hour = hour - self._minute = minute - self._second = second - self._microsecond = microsecond - self._tzinfo = tzinfo - return self - - # Read-only field accessors - @property - def hour(self): - """hour (0-23)""" - return self._hour - - @property - def minute(self): - """minute (0-59)""" - return self._minute - - @property - def second(self): - """second (0-59)""" - return self._second - - @property - def microsecond(self): - """microsecond (0-999999)""" - return self._microsecond - - @property - def tzinfo(self): - """timezone info object""" - return self._tzinfo - - # Standard conversions, __hash__ (and helpers) - - # Comparisons of time objects with other. - - def __eq__(self, other): - if isinstance(other, time): - return self._cmp(other, allow_mixed=True) == 0 - else: - return False - - def __ne__(self, other): - if isinstance(other, time): - return self._cmp(other, allow_mixed=True) != 0 - else: - return True - - def __le__(self, other): - if isinstance(other, time): - return self._cmp(other) <= 0 - else: - _cmperror(self, other) - - def __lt__(self, other): - if isinstance(other, time): - return self._cmp(other) < 0 - else: - _cmperror(self, other) - - def __ge__(self, other): - if isinstance(other, time): - return self._cmp(other) >= 0 - else: - _cmperror(self, other) - - def __gt__(self, other): - if isinstance(other, time): - return self._cmp(other) > 0 - else: - _cmperror(self, other) - - def _cmp(self, other, allow_mixed=False): - assert isinstance(other, time) - mytz = self._tzinfo - ottz = other._tzinfo - myoff = otoff = None - - if mytz is ottz: - base_compare = True - else: - myoff = self.utcoffset() - otoff = other.utcoffset() - base_compare = myoff == otoff - - if base_compare: - return _cmp((self._hour, self._minute, self._second, - self._microsecond), - (other._hour, other._minute, other._second, - other._microsecond)) - if myoff is None or otoff is None: - if allow_mixed: - return 2 # arbitrary non-zero value - else: - raise TypeError("cannot compare naive and aware times") - myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1) - othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1) - return _cmp((myhhmm, self._second, self._microsecond), - (othhmm, other._second, other._microsecond)) - - def __hash__(self): - """Hash.""" - tzoff = self.utcoffset() - if not tzoff: # zero or None - return hash(self._getstate()[0]) - h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff, - timedelta(hours=1)) - assert not m % timedelta(minutes=1), "whole minute" - m //= timedelta(minutes=1) - if 0 <= h < 24: - return hash(time(h, m, self.second, self.microsecond)) - return hash((h, m, self.second, self.microsecond)) - - # Conversion to string - - def _tzstr(self, sep=":"): - """Return formatted timezone offset (+xx:xx) or None.""" - off = self.utcoffset() - if off is not None: - if off.days < 0: - sign = "-" - off = -off - else: - sign = "+" - hh, mm = divmod(off, timedelta(hours=1)) - assert not mm % timedelta(minutes=1), "whole minute" - mm //= timedelta(minutes=1) - assert 0 <= hh < 24 - off = "%s%02d%s%02d" % (sign, hh, sep, mm) - return off - - def __repr__(self): - """Convert to formal string, for repr().""" - if self._microsecond != 0: - s = ", %d, %d" % (self._second, self._microsecond) - elif self._second != 0: - s = ", %d" % self._second - else: - s = "" - s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__, - self._hour, self._minute, s) - if self._tzinfo is not None: - assert s[-1:] == ")" - s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" - return s - - def isoformat(self): - """Return the time formatted according to ISO. - - This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if - self.microsecond == 0. - """ - s = _format_time(self._hour, self._minute, self._second, - self._microsecond) - tz = self._tzstr() - if tz: - s += tz - return s - - __str__ = isoformat - - def strftime(self, fmt): - """Format using strftime(). The date part of the timestamp passed - to underlying strftime should not be used. - """ - # The year must be >= 1000 else Python's strftime implementation - # can raise a bogus exception. - timetuple = (1900, 1, 1, - self._hour, self._minute, self._second, - 0, 1, -1) - return _wrap_strftime(self, fmt, timetuple) - - def __format__(self, fmt): - if len(fmt) != 0: - return self.strftime(fmt) - return str(self) - - # Timezone functions - - def utcoffset(self): - """Return the timezone offset in minutes east of UTC (negative west of - UTC).""" - if self._tzinfo is None: - return None - offset = self._tzinfo.utcoffset(None) - _check_utc_offset("utcoffset", offset) - return offset - - def tzname(self): - """Return the timezone name. - - Note that the name is 100% informational -- there's no requirement that - it mean anything in particular. For example, "GMT", "UTC", "-500", - "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies. - """ - if self._tzinfo is None: - return None - name = self._tzinfo.tzname(None) - _check_tzname(name) - return name - - def dst(self): - """Return 0 if DST is not in effect, or the DST offset (in minutes - eastward) if DST is in effect. - - This is purely informational; the DST offset has already been added to - the UTC offset returned by utcoffset() if applicable, so there's no - need to consult dst() unless you're interested in displaying the DST - info. - """ - if self._tzinfo is None: - return None - offset = self._tzinfo.dst(None) - _check_utc_offset("dst", offset) - return offset - - def replace(self, hour=None, minute=None, second=None, microsecond=None, - tzinfo=True): - """Return a new time with new values for the specified fields.""" - if hour is None: - hour = self.hour - if minute is None: - minute = self.minute - if second is None: - second = self.second - if microsecond is None: - microsecond = self.microsecond - if tzinfo is True: - tzinfo = self.tzinfo - _check_time_fields(hour, minute, second, microsecond) - _check_tzinfo_arg(tzinfo) - return time(hour, minute, second, microsecond, tzinfo) - - def __bool__(self): - if self.second or self.microsecond: - return True - offset = self.utcoffset() or timedelta(0) - return timedelta(hours=self.hour, minutes=self.minute) != offset - - # Pickle support. - - def _getstate(self): - us2, us3 = divmod(self._microsecond, 256) - us1, us2 = divmod(us2, 256) - basestate = bytes([self._hour, self._minute, self._second, - us1, us2, us3]) - if self._tzinfo is None: - return (basestate,) - else: - return (basestate, self._tzinfo) - - def __setstate(self, string, tzinfo): - if len(string) != 6 or string[0] >= 24: - raise TypeError("an integer is required") - (self._hour, self._minute, self._second, - us1, us2, us3) = string - self._microsecond = (((us1 << 8) | us2) << 8) | us3 - if tzinfo is None or isinstance(tzinfo, _tzinfo_class): - self._tzinfo = tzinfo - else: - raise TypeError("bad tzinfo state arg %r" % tzinfo) - - def __reduce__(self): - return (time, self._getstate()) - -_time_class = time # so functions w/ args named "time" can get at the class - -time.min = time(0, 0, 0) -time.max = time(23, 59, 59, 999999) -time.resolution = timedelta(microseconds=1) - -class datetime(date): - """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]) - - The year, month and day arguments are required. tzinfo may be None, or an - instance of a tzinfo subclass. The remaining arguments may be ints. - """ - - __slots__ = date.__slots__ + ( - '_hour', '_minute', '_second', - '_microsecond', '_tzinfo') - def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, - microsecond=0, tzinfo=None): - if isinstance(year, bytes) and len(year) == 10: - # Pickle support - self = date.__new__(cls, year[:4]) - self.__setstate(year, month) - return self - _check_tzinfo_arg(tzinfo) - _check_time_fields(hour, minute, second, microsecond) - self = date.__new__(cls, year, month, day) - self._hour = hour - self._minute = minute - self._second = second - self._microsecond = microsecond - self._tzinfo = tzinfo - return self - - # Read-only field accessors - @property - def hour(self): - """hour (0-23)""" - return self._hour - - @property - def minute(self): - """minute (0-59)""" - return self._minute - - @property - def second(self): - """second (0-59)""" - return self._second - - @property - def microsecond(self): - """microsecond (0-999999)""" - return self._microsecond - - @property - def tzinfo(self): - """timezone info object""" - return self._tzinfo - - @classmethod - def fromtimestamp(cls, t, tz=None): - """Construct a datetime from a POSIX timestamp (like time.time()). - - A timezone info object may be passed in as well. - """ - - _check_tzinfo_arg(tz) - - converter = _time.localtime if tz is None else _time.gmtime - - t, frac = divmod(t, 1.0) - us = int(frac * 1e6) - - # If timestamp is less than one microsecond smaller than a - # full second, us can be rounded up to 1000000. In this case, - # roll over to seconds, otherwise, ValueError is raised - # by the constructor. - if us == 1000000: - t += 1 - us = 0 - y, m, d, hh, mm, ss, weekday, jday, dst = converter(t) - ss = min(ss, 59) # clamp out leap seconds if the platform has them - result = cls(y, m, d, hh, mm, ss, us, tz) - if tz is not None: - result = tz.fromutc(result) - return result - - @classmethod - def utcfromtimestamp(cls, t): - "Construct a UTC datetime from a POSIX timestamp (like time.time())." - t, frac = divmod(t, 1.0) - us = int(frac * 1e6) - - # If timestamp is less than one microsecond smaller than a - # full second, us can be rounded up to 1000000. In this case, - # roll over to seconds, otherwise, ValueError is raised - # by the constructor. - if us == 1000000: - t += 1 - us = 0 - y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t) - ss = min(ss, 59) # clamp out leap seconds if the platform has them - return cls(y, m, d, hh, mm, ss, us) - - # XXX This is supposed to do better than we *can* do by using time.time(), - # XXX if the platform supports a more accurate way. The C implementation - # XXX uses gettimeofday on platforms that have it, but that isn't - # XXX available from Python. So now() may return different results - # XXX across the implementations. - @classmethod - def now(cls, tz=None): - "Construct a datetime from time.time() and optional time zone info." - t = _time.time() - return cls.fromtimestamp(t, tz) - - @classmethod - def utcnow(cls): - "Construct a UTC datetime from time.time()." - t = _time.time() - return cls.utcfromtimestamp(t) - - @classmethod - def combine(cls, date, time): - "Construct a datetime from a given date and a given time." - if not isinstance(date, _date_class): - raise TypeError("date argument must be a date instance") - if not isinstance(time, _time_class): - raise TypeError("time argument must be a time instance") - return cls(date.year, date.month, date.day, - time.hour, time.minute, time.second, time.microsecond, - time.tzinfo) - - def timetuple(self): - "Return local time tuple compatible with time.localtime()." - dst = self.dst() - if dst is None: - dst = -1 - elif dst: - dst = 1 - else: - dst = 0 - return _build_struct_time(self.year, self.month, self.day, - self.hour, self.minute, self.second, - dst) - - def timestamp(self): - "Return POSIX timestamp as float" - if self._tzinfo is None: - return _time.mktime((self.year, self.month, self.day, - self.hour, self.minute, self.second, - -1, -1, -1)) + self.microsecond / 1e6 - else: - return (self - _EPOCH).total_seconds() - - def utctimetuple(self): - "Return UTC time tuple compatible with time.gmtime()." - offset = self.utcoffset() - if offset: - self -= offset - y, m, d = self.year, self.month, self.day - hh, mm, ss = self.hour, self.minute, self.second - return _build_struct_time(y, m, d, hh, mm, ss, 0) - - def date(self): - "Return the date part." - return date(self._year, self._month, self._day) - - def time(self): - "Return the time part, with tzinfo None." - return time(self.hour, self.minute, self.second, self.microsecond) - - def timetz(self): - "Return the time part, with same tzinfo." - return time(self.hour, self.minute, self.second, self.microsecond, - self._tzinfo) - - def replace(self, year=None, month=None, day=None, hour=None, - minute=None, second=None, microsecond=None, tzinfo=True): - """Return a new datetime with new values for the specified fields.""" - if year is None: - year = self.year - if month is None: - month = self.month - if day is None: - day = self.day - if hour is None: - hour = self.hour - if minute is None: - minute = self.minute - if second is None: - second = self.second - if microsecond is None: - microsecond = self.microsecond - if tzinfo is True: - tzinfo = self.tzinfo - _check_date_fields(year, month, day) - _check_time_fields(hour, minute, second, microsecond) - _check_tzinfo_arg(tzinfo) - return datetime(year, month, day, hour, minute, second, - microsecond, tzinfo) - - def astimezone(self, tz=None): - if tz is None: - if self.tzinfo is None: - raise ValueError("astimezone() requires an aware datetime") - ts = (self - _EPOCH) // timedelta(seconds=1) - localtm = _time.localtime(ts) - local = datetime(*localtm[:6]) - try: - # Extract TZ data if available - gmtoff = localtm.tm_gmtoff - zone = localtm.tm_zone - except AttributeError: - # Compute UTC offset and compare with the value implied - # by tm_isdst. If the values match, use the zone name - # implied by tm_isdst. - delta = local - datetime(*_time.gmtime(ts)[:6]) - dst = _time.daylight and localtm.tm_isdst > 0 - gmtoff = -(_time.altzone if dst else _time.timezone) - if delta == timedelta(seconds=gmtoff): - tz = timezone(delta, _time.tzname[dst]) - else: - tz = timezone(delta) - else: - tz = timezone(timedelta(seconds=gmtoff), zone) - - elif not isinstance(tz, tzinfo): - raise TypeError("tz argument must be an instance of tzinfo") - - mytz = self.tzinfo - if mytz is None: - raise ValueError("astimezone() requires an aware datetime") - - if tz is mytz: - return self - - # Convert self to UTC, and attach the new time zone object. - myoffset = self.utcoffset() - if myoffset is None: - raise ValueError("astimezone() requires an aware datetime") - utc = (self - myoffset).replace(tzinfo=tz) - - # Convert from UTC to tz's local time. - return tz.fromutc(utc) - - # Ways to produce a string. - - def ctime(self): - "Return ctime() style string." - weekday = self.toordinal() % 7 or 7 - return "%s %s %2d %02d:%02d:%02d %04d" % ( - _DAYNAMES[weekday], - _MONTHNAMES[self._month], - self._day, - self._hour, self._minute, self._second, - self._year) - - def isoformat(self, sep='T'): - """Return the time formatted according to ISO. - - This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if - self.microsecond == 0. - - If self.tzinfo is not None, the UTC offset is also attached, giving - 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'. - - Optional argument sep specifies the separator between date and - time, default 'T'. - """ - s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, - sep) + - _format_time(self._hour, self._minute, self._second, - self._microsecond)) - off = self.utcoffset() - if off is not None: - if off.days < 0: - sign = "-" - off = -off - else: - sign = "+" - hh, mm = divmod(off, timedelta(hours=1)) - assert not mm % timedelta(minutes=1), "whole minute" - mm //= timedelta(minutes=1) - s += "%s%02d:%02d" % (sign, hh, mm) - return s - - def __repr__(self): - """Convert to formal string, for repr().""" - L = [self._year, self._month, self._day, # These are never zero - self._hour, self._minute, self._second, self._microsecond] - if L[-1] == 0: - del L[-1] - if L[-1] == 0: - del L[-1] - s = ", ".join(map(str, L)) - s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s) - if self._tzinfo is not None: - assert s[-1:] == ")" - s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" - return s - - def __str__(self): - "Convert to string, for str()." - return self.isoformat(sep=' ') - - @classmethod - def strptime(cls, date_string, format): - 'string, format -> new datetime parsed from a string (like time.strptime()).' - import _strptime - return _strptime._strptime_datetime(cls, date_string, format) - - def utcoffset(self): - """Return the timezone offset in minutes east of UTC (negative west of - UTC).""" - if self._tzinfo is None: - return None - offset = self._tzinfo.utcoffset(self) - _check_utc_offset("utcoffset", offset) - return offset - - def tzname(self): - """Return the timezone name. - - Note that the name is 100% informational -- there's no requirement that - it mean anything in particular. For example, "GMT", "UTC", "-500", - "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies. - """ - name = _call_tzinfo_method(self._tzinfo, "tzname", self) - _check_tzname(name) - return name - - def dst(self): - """Return 0 if DST is not in effect, or the DST offset (in minutes - eastward) if DST is in effect. - - This is purely informational; the DST offset has already been added to - the UTC offset returned by utcoffset() if applicable, so there's no - need to consult dst() unless you're interested in displaying the DST - info. - """ - if self._tzinfo is None: - return None - offset = self._tzinfo.dst(self) - _check_utc_offset("dst", offset) - return offset - - # Comparisons of datetime objects with other. - - def __eq__(self, other): - if isinstance(other, datetime): - return self._cmp(other, allow_mixed=True) == 0 - elif not isinstance(other, date): - return NotImplemented - else: - return False - - def __ne__(self, other): - if isinstance(other, datetime): - return self._cmp(other, allow_mixed=True) != 0 - elif not isinstance(other, date): - return NotImplemented - else: - return True - - def __le__(self, other): - if isinstance(other, datetime): - return self._cmp(other) <= 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def __lt__(self, other): - if isinstance(other, datetime): - return self._cmp(other) < 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def __ge__(self, other): - if isinstance(other, datetime): - return self._cmp(other) >= 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def __gt__(self, other): - if isinstance(other, datetime): - return self._cmp(other) > 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def _cmp(self, other, allow_mixed=False): - assert isinstance(other, datetime) - mytz = self._tzinfo - ottz = other._tzinfo - myoff = otoff = None - - if mytz is ottz: - base_compare = True - else: - myoff = self.utcoffset() - otoff = other.utcoffset() - base_compare = myoff == otoff - - if base_compare: - return _cmp((self._year, self._month, self._day, - self._hour, self._minute, self._second, - self._microsecond), - (other._year, other._month, other._day, - other._hour, other._minute, other._second, - other._microsecond)) - if myoff is None or otoff is None: - if allow_mixed: - return 2 # arbitrary non-zero value - else: - raise TypeError("cannot compare naive and aware datetimes") - # XXX What follows could be done more efficiently... - diff = self - other # this will take offsets into account - if diff.days < 0: - return -1 - return diff and 1 or 0 - - def __add__(self, other): - "Add a datetime and a timedelta." - if not isinstance(other, timedelta): - return NotImplemented - delta = timedelta(self.toordinal(), - hours=self._hour, - minutes=self._minute, - seconds=self._second, - microseconds=self._microsecond) - delta += other - hour, rem = divmod(delta.seconds, 3600) - minute, second = divmod(rem, 60) - if 0 < delta.days <= _MAXORDINAL: - return datetime.combine(date.fromordinal(delta.days), - time(hour, minute, second, - delta.microseconds, - tzinfo=self._tzinfo)) - raise OverflowError("result out of range") - - __radd__ = __add__ - - def __sub__(self, other): - "Subtract two datetimes, or a datetime and a timedelta." - if not isinstance(other, datetime): - if isinstance(other, timedelta): - return self + -other - return NotImplemented - - days1 = self.toordinal() - days2 = other.toordinal() - secs1 = self._second + self._minute * 60 + self._hour * 3600 - secs2 = other._second + other._minute * 60 + other._hour * 3600 - base = timedelta(days1 - days2, - secs1 - secs2, - self._microsecond - other._microsecond) - if self._tzinfo is other._tzinfo: - return base - myoff = self.utcoffset() - otoff = other.utcoffset() - if myoff == otoff: - return base - if myoff is None or otoff is None: - raise TypeError("cannot mix naive and timezone-aware time") - return base + otoff - myoff - - def __hash__(self): - tzoff = self.utcoffset() - if tzoff is None: - return hash(self._getstate()[0]) - days = _ymd2ord(self.year, self.month, self.day) - seconds = self.hour * 3600 + self.minute * 60 + self.second - return hash(timedelta(days, seconds, self.microsecond) - tzoff) - - # Pickle support. - - def _getstate(self): - yhi, ylo = divmod(self._year, 256) - us2, us3 = divmod(self._microsecond, 256) - us1, us2 = divmod(us2, 256) - basestate = bytes([yhi, ylo, self._month, self._day, - self._hour, self._minute, self._second, - us1, us2, us3]) - if self._tzinfo is None: - return (basestate,) - else: - return (basestate, self._tzinfo) - - def __setstate(self, string, tzinfo): - (yhi, ylo, self._month, self._day, self._hour, - self._minute, self._second, us1, us2, us3) = string - self._year = yhi * 256 + ylo - self._microsecond = (((us1 << 8) | us2) << 8) | us3 - if tzinfo is None or isinstance(tzinfo, _tzinfo_class): - self._tzinfo = tzinfo - else: - raise TypeError("bad tzinfo state arg %r" % tzinfo) - - def __reduce__(self): - return (self.__class__, self._getstate()) - - -datetime.min = datetime(1, 1, 1) -datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999) -datetime.resolution = timedelta(microseconds=1) - - -def _isoweek1monday(year): - # Helper to calculate the day number of the Monday starting week 1 - # XXX This could be done more efficiently - THURSDAY = 3 - firstday = _ymd2ord(year, 1, 1) - firstweekday = (firstday + 6) % 7 # See weekday() above - week1monday = firstday - firstweekday - if firstweekday > THURSDAY: - week1monday += 7 - return week1monday - -class timezone(tzinfo): - __slots__ = '_offset', '_name' - - # Sentinel value to disallow None - _Omitted = object() - def __new__(cls, offset, name=_Omitted): - if not isinstance(offset, timedelta): - raise TypeError("offset must be a timedelta") - if name is cls._Omitted: - if not offset: - return cls.utc - name = None - elif not isinstance(name, str): - ### - # For Python-Future: - if PY2 and isinstance(name, native_str): - name = name.decode() - else: - raise TypeError("name must be a string") - ### - if not cls._minoffset <= offset <= cls._maxoffset: - raise ValueError("offset must be a timedelta" - " strictly between -timedelta(hours=24) and" - " timedelta(hours=24).") - if (offset.microseconds != 0 or - offset.seconds % 60 != 0): - raise ValueError("offset must be a timedelta" - " representing a whole number of minutes") - return cls._create(offset, name) - - @classmethod - def _create(cls, offset, name=None): - self = tzinfo.__new__(cls) - self._offset = offset - self._name = name - return self - - def __getinitargs__(self): - """pickle support""" - if self._name is None: - return (self._offset,) - return (self._offset, self._name) - - def __eq__(self, other): - if type(other) != timezone: - return False - return self._offset == other._offset - - def __hash__(self): - return hash(self._offset) - - def __repr__(self): - """Convert to formal string, for repr(). - - >>> tz = timezone.utc - >>> repr(tz) - 'datetime.timezone.utc' - >>> tz = timezone(timedelta(hours=-5), 'EST') - >>> repr(tz) - "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')" - """ - if self is self.utc: - return 'datetime.timezone.utc' - if self._name is None: - return "%s(%r)" % ('datetime.' + self.__class__.__name__, - self._offset) - return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__, - self._offset, self._name) - - def __str__(self): - return self.tzname(None) - - def utcoffset(self, dt): - if isinstance(dt, datetime) or dt is None: - return self._offset - raise TypeError("utcoffset() argument must be a datetime instance" - " or None") - - def tzname(self, dt): - if isinstance(dt, datetime) or dt is None: - if self._name is None: - return self._name_from_offset(self._offset) - return self._name - raise TypeError("tzname() argument must be a datetime instance" - " or None") - - def dst(self, dt): - if isinstance(dt, datetime) or dt is None: - return None - raise TypeError("dst() argument must be a datetime instance" - " or None") - - def fromutc(self, dt): - if isinstance(dt, datetime): - if dt.tzinfo is not self: - raise ValueError("fromutc: dt.tzinfo " - "is not self") - return dt + self._offset - raise TypeError("fromutc() argument must be a datetime instance" - " or None") - - _maxoffset = timedelta(hours=23, minutes=59) - _minoffset = -_maxoffset - - @staticmethod - def _name_from_offset(delta): - if delta < timedelta(0): - sign = '-' - delta = -delta - else: - sign = '+' - hours, rest = divmod(delta, timedelta(hours=1)) - minutes = rest // timedelta(minutes=1) - return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes) - -timezone.utc = timezone._create(timedelta(0)) -timezone.min = timezone._create(timezone._minoffset) -timezone.max = timezone._create(timezone._maxoffset) -_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) -""" -Some time zone algebra. For a datetime x, let - x.n = x stripped of its timezone -- its naive time. - x.o = x.utcoffset(), and assuming that doesn't raise an exception or - return None - x.d = x.dst(), and assuming that doesn't raise an exception or - return None - x.s = x's standard offset, x.o - x.d - -Now some derived rules, where k is a duration (timedelta). - -1. x.o = x.s + x.d - This follows from the definition of x.s. - -2. If x and y have the same tzinfo member, x.s = y.s. - This is actually a requirement, an assumption we need to make about - sane tzinfo classes. - -3. The naive UTC time corresponding to x is x.n - x.o. - This is again a requirement for a sane tzinfo class. - -4. (x+k).s = x.s - This follows from #2, and that datimetimetz+timedelta preserves tzinfo. - -5. (x+k).n = x.n + k - Again follows from how arithmetic is defined. - -Now we can explain tz.fromutc(x). Let's assume it's an interesting case -(meaning that the various tzinfo methods exist, and don't blow up or return -None when called). - -The function wants to return a datetime y with timezone tz, equivalent to x. -x is already in UTC. - -By #3, we want - - y.n - y.o = x.n [1] - -The algorithm starts by attaching tz to x.n, and calling that y. So -x.n = y.n at the start. Then it wants to add a duration k to y, so that [1] -becomes true; in effect, we want to solve [2] for k: - - (y+k).n - (y+k).o = x.n [2] - -By #1, this is the same as - - (y+k).n - ((y+k).s + (y+k).d) = x.n [3] - -By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start. -Substituting that into [3], - - x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving - k - (y+k).s - (y+k).d = 0; rearranging, - k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so - k = y.s - (y+k).d - -On the RHS, (y+k).d can't be computed directly, but y.s can be, and we -approximate k by ignoring the (y+k).d term at first. Note that k can't be -very large, since all offset-returning methods return a duration of magnitude -less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must -be 0, so ignoring it has no consequence then. - -In any case, the new value is - - z = y + y.s [4] - -It's helpful to step back at look at [4] from a higher level: it's simply -mapping from UTC to tz's standard time. - -At this point, if - - z.n - z.o = x.n [5] - -we have an equivalent time, and are almost done. The insecurity here is -at the start of daylight time. Picture US Eastern for concreteness. The wall -time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good -sense then. The docs ask that an Eastern tzinfo class consider such a time to -be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST -on the day DST starts. We want to return the 1:MM EST spelling because that's -the only spelling that makes sense on the local wall clock. - -In fact, if [5] holds at this point, we do have the standard-time spelling, -but that takes a bit of proof. We first prove a stronger result. What's the -difference between the LHS and RHS of [5]? Let - - diff = x.n - (z.n - z.o) [6] - -Now - z.n = by [4] - (y + y.s).n = by #5 - y.n + y.s = since y.n = x.n - x.n + y.s = since z and y are have the same tzinfo member, - y.s = z.s by #2 - x.n + z.s - -Plugging that back into [6] gives - - diff = - x.n - ((x.n + z.s) - z.o) = expanding - x.n - x.n - z.s + z.o = cancelling - - z.s + z.o = by #2 - z.d - -So diff = z.d. - -If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time -spelling we wanted in the endcase described above. We're done. Contrarily, -if z.d = 0, then we have a UTC equivalent, and are also done. - -If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to -add to z (in effect, z is in tz's standard time, and we need to shift the -local clock into tz's daylight time). - -Let - - z' = z + z.d = z + diff [7] - -and we can again ask whether - - z'.n - z'.o = x.n [8] - -If so, we're done. If not, the tzinfo class is insane, according to the -assumptions we've made. This also requires a bit of proof. As before, let's -compute the difference between the LHS and RHS of [8] (and skipping some of -the justifications for the kinds of substitutions we've done several times -already): - - diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7] - x.n - (z.n + diff - z'.o) = replacing diff via [6] - x.n - (z.n + x.n - (z.n - z.o) - z'.o) = - x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n - - z.n + z.n - z.o + z'.o = cancel z.n - - z.o + z'.o = #1 twice - -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo - z'.d - z.d - -So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal, -we've found the UTC-equivalent so are done. In fact, we stop with [7] and -return z', not bothering to compute z'.d. - -How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by -a dst() offset, and starting *from* a time already in DST (we know z.d != 0), -would have to change the result dst() returns: we start in DST, and moving -a little further into it takes us out of DST. - -There isn't a sane case where this can happen. The closest it gets is at -the end of DST, where there's an hour in UTC with no spelling in a hybrid -tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During -that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM -UTC) because the docs insist on that, but 0:MM is taken as being in daylight -time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local -clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in -standard time. Since that's what the local clock *does*, we want to map both -UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous -in local time, but so it goes -- it's the way the local clock works. - -When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0, -so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going. -z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8] -(correctly) concludes that z' is not UTC-equivalent to x. - -Because we know z.d said z was in daylight time (else [5] would have held and -we would have stopped then), and we know z.d != z'.d (else [8] would have held -and we have stopped then), and there are only 2 possible values dst() can -return in Eastern, it follows that z'.d must be 0 (which it is in the example, -but the reasoning doesn't depend on the example -- it depends on there being -two possible dst() outcomes, one zero and the other non-zero). Therefore -z' must be in standard time, and is the spelling we want in this case. - -Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is -concerned (because it takes z' as being in standard time rather than the -daylight time we intend here), but returning it gives the real-life "local -clock repeats an hour" behavior when mapping the "unspellable" UTC hour into -tz. - -When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with -the 1:MM standard time spelling we want. - -So how can this break? One of the assumptions must be violated. Two -possibilities: - -1) [2] effectively says that y.s is invariant across all y belong to a given - time zone. This isn't true if, for political reasons or continental drift, - a region decides to change its base offset from UTC. - -2) There may be versions of "double daylight" time where the tail end of - the analysis gives up a step too early. I haven't thought about that - enough to say. - -In any case, it's clear that the default fromutc() is strong enough to handle -"almost all" time zones: so long as the standard offset is invariant, it -doesn't matter if daylight time transition points change from year to year, or -if daylight time is skipped in some years; it doesn't matter how large or -small dst() may get within its bounds; and it doesn't even matter if some -perverse time zone returns a negative dst()). So a breaking case must be -pretty bizarre, and a tzinfo subclass can override fromutc() if it is. -""" -try: - from _datetime import * -except ImportError: - pass -else: - # Clean up unused names - del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, - _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES, - _build_struct_time, _call_tzinfo_method, _check_date_fields, - _check_time_fields, _check_tzinfo_arg, _check_tzname, - _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month, - _days_before_year, _days_in_month, _format_time, _is_leap, - _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class, - _wrap_strftime, _ymd2ord) - # XXX Since import * above excludes names that start with _, - # docstring does not get overwritten. In the future, it may be - # appropriate to maintain a single module level docstring and - # remove the following line. - from _datetime import __doc__ diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/base64mime.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/base64mime.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/base64mime.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/base64mime.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Base64 content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode arbitrary 8-bit data using the three 8-bit bytes in four 7-bit -characters encoding known as Base64. - -It is used in the MIME standards for email to attach images, audio, and text -using some 8-bit character sets to messages. - -This module provides an interface to encode and decode both headers and bodies -with Base64 encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:, From:, Cc:, etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character conversion -necessary for proper internationalized headers; it only does dumb encoding and -decoding. To deal with the various line wrapping issues, use the email.header -module. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import range -from future.builtins import bytes - -__all__ = [ - 'body_decode', - 'body_encode', - 'decode', - 'decodestring', - 'header_encode', - 'header_length', - ] - - -from base64 import b64encode -from binascii import b2a_base64, a2b_base64 - -CRLF = '\r\n' -NL = '\n' -EMPTYSTRING = '' - -# See also Charset.py -MISC_LEN = 7 - - -# Helpers -def header_length(bytearray): - """Return the length of s when it is encoded with base64.""" - groups_of_3, leftover = divmod(len(bytearray), 3) - # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. - n = groups_of_3 * 4 - if leftover: - n += 4 - return n - - -def header_encode(header_bytes, charset='iso-8859-1'): - """Encode a single header line with Base64 encoding in a given charset. - - charset names the character set to use to encode the header. It defaults - to iso-8859-1. Base64 encoding is defined in RFC 2045. - """ - if not header_bytes: - return "" - if isinstance(header_bytes, str): - header_bytes = header_bytes.encode(charset) - encoded = b64encode(header_bytes).decode("ascii") - return '=?%s?b?%s?=' % (charset, encoded) - - -def body_encode(s, maxlinelen=76, eol=NL): - r"""Encode a string with base64. - - Each line will be wrapped at, at most, maxlinelen characters (defaults to - 76 characters). - - Each line of encoded text will end with eol, which defaults to "\n". Set - this to "\r\n" if you will be using the result of this function directly - in an email. - """ - if not s: - return s - - encvec = [] - max_unencoded = maxlinelen * 3 // 4 - for i in range(0, len(s), max_unencoded): - # BAW: should encode() inherit b2a_base64()'s dubious behavior in - # adding a newline to the encoded string? - enc = b2a_base64(s[i:i + max_unencoded]).decode("ascii") - if enc.endswith(NL) and eol != NL: - enc = enc[:-1] + eol - encvec.append(enc) - return EMPTYSTRING.join(encvec) - - -def decode(string): - """Decode a raw base64 string, returning a bytes object. - - This function does not parse a full MIME header value encoded with - base64 (like =?iso-8895-1?b?bmloISBuaWgh?=) -- please use the high - level email.header class for that functionality. - """ - if not string: - return bytes() - elif isinstance(string, str): - return a2b_base64(string.encode('raw-unicode-escape')) - else: - return a2b_base64(string) - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/charset.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/charset.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/charset.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/charset.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,409 +0,0 @@ -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import str -from future.builtins import next - -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -__all__ = [ - 'Charset', - 'add_alias', - 'add_charset', - 'add_codec', - ] - -from functools import partial - -from future.backports import email -from future.backports.email import errors -from future.backports.email.encoders import encode_7or8bit - - -# Flags for types of header encodings -QP = 1 # Quoted-Printable -BASE64 = 2 # Base64 -SHORTEST = 3 # the shorter of QP and base64, but only for headers - -# In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7 -RFC2047_CHROME_LEN = 7 - -DEFAULT_CHARSET = 'us-ascii' -UNKNOWN8BIT = 'unknown-8bit' -EMPTYSTRING = '' - - -# Defaults -CHARSETS = { - # input header enc body enc output conv - 'iso-8859-1': (QP, QP, None), - 'iso-8859-2': (QP, QP, None), - 'iso-8859-3': (QP, QP, None), - 'iso-8859-4': (QP, QP, None), - # iso-8859-5 is Cyrillic, and not especially used - # iso-8859-6 is Arabic, also not particularly used - # iso-8859-7 is Greek, QP will not make it readable - # iso-8859-8 is Hebrew, QP will not make it readable - 'iso-8859-9': (QP, QP, None), - 'iso-8859-10': (QP, QP, None), - # iso-8859-11 is Thai, QP will not make it readable - 'iso-8859-13': (QP, QP, None), - 'iso-8859-14': (QP, QP, None), - 'iso-8859-15': (QP, QP, None), - 'iso-8859-16': (QP, QP, None), - 'windows-1252':(QP, QP, None), - 'viscii': (QP, QP, None), - 'us-ascii': (None, None, None), - 'big5': (BASE64, BASE64, None), - 'gb2312': (BASE64, BASE64, None), - 'euc-jp': (BASE64, None, 'iso-2022-jp'), - 'shift_jis': (BASE64, None, 'iso-2022-jp'), - 'iso-2022-jp': (BASE64, None, None), - 'koi8-r': (BASE64, BASE64, None), - 'utf-8': (SHORTEST, BASE64, 'utf-8'), - } - -# Aliases for other commonly-used names for character sets. Map -# them to the real ones used in email. -ALIASES = { - 'latin_1': 'iso-8859-1', - 'latin-1': 'iso-8859-1', - 'latin_2': 'iso-8859-2', - 'latin-2': 'iso-8859-2', - 'latin_3': 'iso-8859-3', - 'latin-3': 'iso-8859-3', - 'latin_4': 'iso-8859-4', - 'latin-4': 'iso-8859-4', - 'latin_5': 'iso-8859-9', - 'latin-5': 'iso-8859-9', - 'latin_6': 'iso-8859-10', - 'latin-6': 'iso-8859-10', - 'latin_7': 'iso-8859-13', - 'latin-7': 'iso-8859-13', - 'latin_8': 'iso-8859-14', - 'latin-8': 'iso-8859-14', - 'latin_9': 'iso-8859-15', - 'latin-9': 'iso-8859-15', - 'latin_10':'iso-8859-16', - 'latin-10':'iso-8859-16', - 'cp949': 'ks_c_5601-1987', - 'euc_jp': 'euc-jp', - 'euc_kr': 'euc-kr', - 'ascii': 'us-ascii', - } - - -# Map charsets to their Unicode codec strings. -CODEC_MAP = { - 'gb2312': 'eucgb2312_cn', - 'big5': 'big5_tw', - # Hack: We don't want *any* conversion for stuff marked us-ascii, as all - # sorts of garbage might be sent to us in the guise of 7-bit us-ascii. - # Let that stuff pass through without conversion to/from Unicode. - 'us-ascii': None, - } - - -# Convenience functions for extending the above mappings -def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): - """Add character set properties to the global registry. - - charset is the input character set, and must be the canonical name of a - character set. - - Optional header_enc and body_enc is either Charset.QP for - quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for - the shortest of qp or base64 encoding, or None for no encoding. SHORTEST - is only valid for header_enc. It describes how message headers and - message bodies in the input charset are to be encoded. Default is no - encoding. - - Optional output_charset is the character set that the output should be - in. Conversions will proceed from input charset, to Unicode, to the - output charset when the method Charset.convert() is called. The default - is to output in the same character set as the input. - - Both input_charset and output_charset must have Unicode codec entries in - the module's charset-to-codec mapping; use add_codec(charset, codecname) - to add codecs the module does not know about. See the codecs module's - documentation for more information. - """ - if body_enc == SHORTEST: - raise ValueError('SHORTEST not allowed for body_enc') - CHARSETS[charset] = (header_enc, body_enc, output_charset) - - -def add_alias(alias, canonical): - """Add a character set alias. - - alias is the alias name, e.g. latin-1 - canonical is the character set's canonical name, e.g. iso-8859-1 - """ - ALIASES[alias] = canonical - - -def add_codec(charset, codecname): - """Add a codec that map characters in the given charset to/from Unicode. - - charset is the canonical name of a character set. codecname is the name - of a Python codec, as appropriate for the second argument to the unicode() - built-in, or to the encode() method of a Unicode string. - """ - CODEC_MAP[charset] = codecname - - -# Convenience function for encoding strings, taking into account -# that they might be unknown-8bit (ie: have surrogate-escaped bytes) -def _encode(string, codec): - string = str(string) - if codec == UNKNOWN8BIT: - return string.encode('ascii', 'surrogateescape') - else: - return string.encode(codec) - - -class Charset(object): - """Map character sets to their email properties. - - This class provides information about the requirements imposed on email - for a specific character set. It also provides convenience routines for - converting between character sets, given the availability of the - applicable codecs. Given a character set, it will do its best to provide - information on how to use that character set in an email in an - RFC-compliant way. - - Certain character sets must be encoded with quoted-printable or base64 - when used in email headers or bodies. Certain character sets must be - converted outright, and are not allowed in email. Instances of this - module expose the following information about a character set: - - input_charset: The initial character set specified. Common aliases - are converted to their `official' email names (e.g. latin_1 - is converted to iso-8859-1). Defaults to 7-bit us-ascii. - - header_encoding: If the character set must be encoded before it can be - used in an email header, this attribute will be set to - Charset.QP (for quoted-printable), Charset.BASE64 (for - base64 encoding), or Charset.SHORTEST for the shortest of - QP or BASE64 encoding. Otherwise, it will be None. - - body_encoding: Same as header_encoding, but describes the encoding for the - mail message's body, which indeed may be different than the - header encoding. Charset.SHORTEST is not allowed for - body_encoding. - - output_charset: Some character sets must be converted before they can be - used in email headers or bodies. If the input_charset is - one of them, this attribute will contain the name of the - charset output will be converted to. Otherwise, it will - be None. - - input_codec: The name of the Python codec used to convert the - input_charset to Unicode. If no conversion codec is - necessary, this attribute will be None. - - output_codec: The name of the Python codec used to convert Unicode - to the output_charset. If no conversion codec is necessary, - this attribute will have the same value as the input_codec. - """ - def __init__(self, input_charset=DEFAULT_CHARSET): - # RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to - # unicode because its .lower() is locale insensitive. If the argument - # is already a unicode, we leave it at that, but ensure that the - # charset is ASCII, as the standard (RFC XXX) requires. - try: - if isinstance(input_charset, str): - input_charset.encode('ascii') - else: - input_charset = str(input_charset, 'ascii') - except UnicodeError: - raise errors.CharsetError(input_charset) - input_charset = input_charset.lower() - # Set the input charset after filtering through the aliases - self.input_charset = ALIASES.get(input_charset, input_charset) - # We can try to guess which encoding and conversion to use by the - # charset_map dictionary. Try that first, but let the user override - # it. - henc, benc, conv = CHARSETS.get(self.input_charset, - (SHORTEST, BASE64, None)) - if not conv: - conv = self.input_charset - # Set the attributes, allowing the arguments to override the default. - self.header_encoding = henc - self.body_encoding = benc - self.output_charset = ALIASES.get(conv, conv) - # Now set the codecs. If one isn't defined for input_charset, - # guess and try a Unicode codec with the same name as input_codec. - self.input_codec = CODEC_MAP.get(self.input_charset, - self.input_charset) - self.output_codec = CODEC_MAP.get(self.output_charset, - self.output_charset) - - def __str__(self): - return self.input_charset.lower() - - __repr__ = __str__ - - def __eq__(self, other): - return str(self) == str(other).lower() - - def __ne__(self, other): - return not self.__eq__(other) - - def get_body_encoding(self): - """Return the content-transfer-encoding used for body encoding. - - This is either the string `quoted-printable' or `base64' depending on - the encoding used, or it is a function in which case you should call - the function with a single argument, the Message object being - encoded. The function should then set the Content-Transfer-Encoding - header itself to whatever is appropriate. - - Returns "quoted-printable" if self.body_encoding is QP. - Returns "base64" if self.body_encoding is BASE64. - Returns conversion function otherwise. - """ - assert self.body_encoding != SHORTEST - if self.body_encoding == QP: - return 'quoted-printable' - elif self.body_encoding == BASE64: - return 'base64' - else: - return encode_7or8bit - - def get_output_charset(self): - """Return the output character set. - - This is self.output_charset if that is not None, otherwise it is - self.input_charset. - """ - return self.output_charset or self.input_charset - - def header_encode(self, string): - """Header-encode a string by converting it first to bytes. - - The type of encoding (base64 or quoted-printable) will be based on - this charset's `header_encoding`. - - :param string: A unicode string for the header. It must be possible - to encode this string to bytes using the character set's - output codec. - :return: The encoded string, with RFC 2047 chrome. - """ - codec = self.output_codec or 'us-ascii' - header_bytes = _encode(string, codec) - # 7bit/8bit encodings return the string unchanged (modulo conversions) - encoder_module = self._get_encoder(header_bytes) - if encoder_module is None: - return string - return encoder_module.header_encode(header_bytes, codec) - - def header_encode_lines(self, string, maxlengths): - """Header-encode a string by converting it first to bytes. - - This is similar to `header_encode()` except that the string is fit - into maximum line lengths as given by the argument. - - :param string: A unicode string for the header. It must be possible - to encode this string to bytes using the character set's - output codec. - :param maxlengths: Maximum line length iterator. Each element - returned from this iterator will provide the next maximum line - length. This parameter is used as an argument to built-in next() - and should never be exhausted. The maximum line lengths should - not count the RFC 2047 chrome. These line lengths are only a - hint; the splitter does the best it can. - :return: Lines of encoded strings, each with RFC 2047 chrome. - """ - # See which encoding we should use. - codec = self.output_codec or 'us-ascii' - header_bytes = _encode(string, codec) - encoder_module = self._get_encoder(header_bytes) - encoder = partial(encoder_module.header_encode, charset=codec) - # Calculate the number of characters that the RFC 2047 chrome will - # contribute to each line. - charset = self.get_output_charset() - extra = len(charset) + RFC2047_CHROME_LEN - # Now comes the hard part. We must encode bytes but we can't split on - # bytes because some character sets are variable length and each - # encoded word must stand on its own. So the problem is you have to - # encode to bytes to figure out this word's length, but you must split - # on characters. This causes two problems: first, we don't know how - # many octets a specific substring of unicode characters will get - # encoded to, and second, we don't know how many ASCII characters - # those octets will get encoded to. Unless we try it. Which seems - # inefficient. In the interest of being correct rather than fast (and - # in the hope that there will be few encoded headers in any such - # message), brute force it. :( - lines = [] - current_line = [] - maxlen = next(maxlengths) - extra - for character in string: - current_line.append(character) - this_line = EMPTYSTRING.join(current_line) - length = encoder_module.header_length(_encode(this_line, charset)) - if length > maxlen: - # This last character doesn't fit so pop it off. - current_line.pop() - # Does nothing fit on the first line? - if not lines and not current_line: - lines.append(None) - else: - separator = (' ' if lines else '') - joined_line = EMPTYSTRING.join(current_line) - header_bytes = _encode(joined_line, codec) - lines.append(encoder(header_bytes)) - current_line = [character] - maxlen = next(maxlengths) - extra - joined_line = EMPTYSTRING.join(current_line) - header_bytes = _encode(joined_line, codec) - lines.append(encoder(header_bytes)) - return lines - - def _get_encoder(self, header_bytes): - if self.header_encoding == BASE64: - return email.base64mime - elif self.header_encoding == QP: - return email.quoprimime - elif self.header_encoding == SHORTEST: - len64 = email.base64mime.header_length(header_bytes) - lenqp = email.quoprimime.header_length(header_bytes) - if len64 < lenqp: - return email.base64mime - else: - return email.quoprimime - else: - return None - - def body_encode(self, string): - """Body-encode a string by converting it first to bytes. - - The type of encoding (base64 or quoted-printable) will be based on - self.body_encoding. If body_encoding is None, we assume the - output charset is a 7bit encoding, so re-encoding the decoded - string using the ascii codec produces the correct string version - of the content. - """ - if not string: - return string - if self.body_encoding is BASE64: - if isinstance(string, str): - string = string.encode(self.output_charset) - return email.base64mime.body_encode(string) - elif self.body_encoding is QP: - # quopromime.body_encode takes a string, but operates on it as if - # it were a list of byte codes. For a (minimal) history on why - # this is so, see changeset 0cf700464177. To correctly encode a - # character set, then, we must turn it into pseudo bytes via the - # latin1 charset, which will encode any byte as a single code point - # between 0 and 255, which is what body_encode is expecting. - if isinstance(string, str): - string = string.encode(self.output_charset) - string = string.decode('latin1') - return email.quoprimime.body_encode(string) - else: - if isinstance(string, str): - string = string.encode(self.output_charset).decode('ascii') - return string diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_encoded_words.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_encoded_words.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_encoded_words.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_encoded_words.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,232 +0,0 @@ -""" Routines for manipulating RFC2047 encoded words. - -This is currently a package-private API, but will be considered for promotion -to a public API if there is demand. - -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes -from future.builtins import chr -from future.builtins import int -from future.builtins import str - -# An ecoded word looks like this: -# -# =?charset[*lang]?cte?encoded_string?= -# -# for more information about charset see the charset module. Here it is one -# of the preferred MIME charset names (hopefully; you never know when parsing). -# cte (Content Transfer Encoding) is either 'q' or 'b' (ignoring case). In -# theory other letters could be used for other encodings, but in practice this -# (almost?) never happens. There could be a public API for adding entries -# to the CTE tables, but YAGNI for now. 'q' is Quoted Printable, 'b' is -# Base64. The meaning of encoded_string should be obvious. 'lang' is optional -# as indicated by the brackets (they are not part of the syntax) but is almost -# never encountered in practice. -# -# The general interface for a CTE decoder is that it takes the encoded_string -# as its argument, and returns a tuple (cte_decoded_string, defects). The -# cte_decoded_string is the original binary that was encoded using the -# specified cte. 'defects' is a list of MessageDefect instances indicating any -# problems encountered during conversion. 'charset' and 'lang' are the -# corresponding strings extracted from the EW, case preserved. -# -# The general interface for a CTE encoder is that it takes a binary sequence -# as input and returns the cte_encoded_string, which is an ascii-only string. -# -# Each decoder must also supply a length function that takes the binary -# sequence as its argument and returns the length of the resulting encoded -# string. -# -# The main API functions for the module are decode, which calls the decoder -# referenced by the cte specifier, and encode, which adds the appropriate -# RFC 2047 "chrome" to the encoded string, and can optionally automatically -# select the shortest possible encoding. See their docstrings below for -# details. - -import re -import base64 -import binascii -import functools -from string import ascii_letters, digits -from future.backports.email import errors - -__all__ = ['decode_q', - 'encode_q', - 'decode_b', - 'encode_b', - 'len_q', - 'len_b', - 'decode', - 'encode', - ] - -# -# Quoted Printable -# - -# regex based decoder. -_q_byte_subber = functools.partial(re.compile(br'=([a-fA-F0-9]{2})').sub, - lambda m: bytes([int(m.group(1), 16)])) - -def decode_q(encoded): - encoded = bytes(encoded.replace(b'_', b' ')) - return _q_byte_subber(encoded), [] - - -# dict mapping bytes to their encoded form -class _QByteMap(dict): - - safe = bytes(b'-!*+/' + ascii_letters.encode('ascii') + digits.encode('ascii')) - - def __missing__(self, key): - if key in self.safe: - self[key] = chr(key) - else: - self[key] = "={:02X}".format(key) - return self[key] - -_q_byte_map = _QByteMap() - -# In headers spaces are mapped to '_'. -_q_byte_map[ord(' ')] = '_' - -def encode_q(bstring): - return str(''.join(_q_byte_map[x] for x in bytes(bstring))) - -def len_q(bstring): - return sum(len(_q_byte_map[x]) for x in bytes(bstring)) - - -# -# Base64 -# - -def decode_b(encoded): - defects = [] - pad_err = len(encoded) % 4 - if pad_err: - defects.append(errors.InvalidBase64PaddingDefect()) - padded_encoded = encoded + b'==='[:4-pad_err] - else: - padded_encoded = encoded - try: - # The validate kwarg to b64decode is not supported in Py2.x - if not re.match(b'^[A-Za-z0-9+/]*={0,2}$', padded_encoded): - raise binascii.Error('Non-base64 digit found') - return base64.b64decode(padded_encoded), defects - except binascii.Error: - # Since we had correct padding, this must an invalid char error. - defects = [errors.InvalidBase64CharactersDefect()] - # The non-alphabet characters are ignored as far as padding - # goes, but we don't know how many there are. So we'll just - # try various padding lengths until something works. - for i in 0, 1, 2, 3: - try: - return base64.b64decode(encoded+b'='*i), defects - except (binascii.Error, TypeError): # Py2 raises a TypeError - if i==0: - defects.append(errors.InvalidBase64PaddingDefect()) - else: - # This should never happen. - raise AssertionError("unexpected binascii.Error") - -def encode_b(bstring): - return base64.b64encode(bstring).decode('ascii') - -def len_b(bstring): - groups_of_3, leftover = divmod(len(bstring), 3) - # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. - return groups_of_3 * 4 + (4 if leftover else 0) - - -_cte_decoders = { - 'q': decode_q, - 'b': decode_b, - } - -def decode(ew): - """Decode encoded word and return (string, charset, lang, defects) tuple. - - An RFC 2047/2243 encoded word has the form: - - =?charset*lang?cte?encoded_string?= - - where '*lang' may be omitted but the other parts may not be. - - This function expects exactly such a string (that is, it does not check the - syntax and may raise errors if the string is not well formed), and returns - the encoded_string decoded first from its Content Transfer Encoding and - then from the resulting bytes into unicode using the specified charset. If - the cte-decoded string does not successfully decode using the specified - character set, a defect is added to the defects list and the unknown octets - are replaced by the unicode 'unknown' character \uFDFF. - - The specified charset and language are returned. The default for language, - which is rarely if ever encountered, is the empty string. - - """ - _, charset, cte, cte_string, _ = str(ew).split('?') - charset, _, lang = charset.partition('*') - cte = cte.lower() - # Recover the original bytes and do CTE decoding. - bstring = cte_string.encode('ascii', 'surrogateescape') - bstring, defects = _cte_decoders[cte](bstring) - # Turn the CTE decoded bytes into unicode. - try: - string = bstring.decode(charset) - except UnicodeError: - defects.append(errors.UndecodableBytesDefect("Encoded word " - "contains bytes not decodable using {} charset".format(charset))) - string = bstring.decode(charset, 'surrogateescape') - except LookupError: - string = bstring.decode('ascii', 'surrogateescape') - if charset.lower() != 'unknown-8bit': - defects.append(errors.CharsetError("Unknown charset {} " - "in encoded word; decoded as unknown bytes".format(charset))) - return string, charset, lang, defects - - -_cte_encoders = { - 'q': encode_q, - 'b': encode_b, - } - -_cte_encode_length = { - 'q': len_q, - 'b': len_b, - } - -def encode(string, charset='utf-8', encoding=None, lang=''): - """Encode string using the CTE encoding that produces the shorter result. - - Produces an RFC 2047/2243 encoded word of the form: - - =?charset*lang?cte?encoded_string?= - - where '*lang' is omitted unless the 'lang' parameter is given a value. - Optional argument charset (defaults to utf-8) specifies the charset to use - to encode the string to binary before CTE encoding it. Optional argument - 'encoding' is the cte specifier for the encoding that should be used ('q' - or 'b'); if it is None (the default) the encoding which produces the - shortest encoded sequence is used, except that 'q' is preferred if it is up - to five characters longer. Optional argument 'lang' (default '') gives the - RFC 2243 language string to specify in the encoded word. - - """ - string = str(string) - if charset == 'unknown-8bit': - bstring = string.encode('ascii', 'surrogateescape') - else: - bstring = string.encode(charset) - if encoding is None: - qlen = _cte_encode_length['q'](bstring) - blen = _cte_encode_length['b'](bstring) - # Bias toward q. 5 is arbitrary. - encoding = 'q' if qlen - blen < 5 else 'b' - encoded = _cte_encoders[encoding](bstring) - if lang: - lang = '*' + lang - return "=?{0}{1}?{2}?{3}?=".format(charset, lang, encoding, encoded) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/encoders.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/encoders.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/encoders.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/encoders.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Encodings and related functions.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import str - -__all__ = [ - 'encode_7or8bit', - 'encode_base64', - 'encode_noop', - 'encode_quopri', - ] - - -try: - from base64 import encodebytes as _bencode -except ImportError: - # Py2 compatibility. TODO: test this! - from base64 import encodestring as _bencode -from quopri import encodestring as _encodestring - - -def _qencode(s): - enc = _encodestring(s, quotetabs=True) - # Must encode spaces, which quopri.encodestring() doesn't do - return enc.replace(' ', '=20') - - -def encode_base64(msg): - """Encode the message's payload in Base64. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = str(_bencode(orig), 'ascii') - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'base64' - - -def encode_quopri(msg): - """Encode the message's payload in quoted-printable. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = _qencode(orig) - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'quoted-printable' - - -def encode_7or8bit(msg): - """Set the Content-Transfer-Encoding header to 7bit or 8bit.""" - orig = msg.get_payload() - if orig is None: - # There's no payload. For backwards compatibility we use 7bit - msg['Content-Transfer-Encoding'] = '7bit' - return - # We play a trick to make this go fast. If encoding/decode to ASCII - # succeeds, we know the data must be 7bit, otherwise treat it as 8bit. - try: - if isinstance(orig, str): - orig.encode('ascii') - else: - orig.decode('ascii') - except UnicodeError: - charset = msg.get_charset() - output_cset = charset and charset.output_charset - # iso-2022-* is non-ASCII but encodes to a 7-bit representation - if output_cset and output_cset.lower().startswith('iso-2022-'): - msg['Content-Transfer-Encoding'] = '7bit' - else: - msg['Content-Transfer-Encoding'] = '8bit' - else: - msg['Content-Transfer-Encoding'] = '7bit' - if not isinstance(orig, str): - msg.set_payload(orig.decode('ascii', 'surrogateescape')) - - -def encode_noop(msg): - """Do nothing.""" - # Well, not quite *nothing*: in Python3 we have to turn bytes into a string - # in our internal surrogateescaped form in order to keep the model - # consistent. - orig = msg.get_payload() - if not isinstance(orig, str): - msg.set_payload(orig.decode('ascii', 'surrogateescape')) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/errors.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/errors.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/errors.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/errors.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""email package exception classes.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super - - -class MessageError(Exception): - """Base class for errors in the email package.""" - - -class MessageParseError(MessageError): - """Base class for message parsing errors.""" - - -class HeaderParseError(MessageParseError): - """Error while parsing headers.""" - - -class BoundaryError(MessageParseError): - """Couldn't find terminating boundary.""" - - -class MultipartConversionError(MessageError, TypeError): - """Conversion to a multipart is prohibited.""" - - -class CharsetError(MessageError): - """An illegal charset was given.""" - - -# These are parsing defects which the parser was able to work around. -class MessageDefect(ValueError): - """Base class for a message defect.""" - - def __init__(self, line=None): - if line is not None: - super().__init__(line) - self.line = line - -class NoBoundaryInMultipartDefect(MessageDefect): - """A message claimed to be a multipart but had no boundary parameter.""" - -class StartBoundaryNotFoundDefect(MessageDefect): - """The claimed start boundary was never found.""" - -class CloseBoundaryNotFoundDefect(MessageDefect): - """A start boundary was found, but not the corresponding close boundary.""" - -class FirstHeaderLineIsContinuationDefect(MessageDefect): - """A message had a continuation line as its first header line.""" - -class MisplacedEnvelopeHeaderDefect(MessageDefect): - """A 'Unix-from' header was found in the middle of a header block.""" - -class MissingHeaderBodySeparatorDefect(MessageDefect): - """Found line with no leading whitespace and no colon before blank line.""" -# XXX: backward compatibility, just in case (it was never emitted). -MalformedHeaderDefect = MissingHeaderBodySeparatorDefect - -class MultipartInvariantViolationDefect(MessageDefect): - """A message claimed to be a multipart but no subparts were found.""" - -class InvalidMultipartContentTransferEncodingDefect(MessageDefect): - """An invalid content transfer encoding was set on the multipart itself.""" - -class UndecodableBytesDefect(MessageDefect): - """Header contained bytes that could not be decoded""" - -class InvalidBase64PaddingDefect(MessageDefect): - """base64 encoded sequence had an incorrect length""" - -class InvalidBase64CharactersDefect(MessageDefect): - """base64 encoded sequence had characters not in base64 alphabet""" - -# These errors are specific to header parsing. - -class HeaderDefect(MessageDefect): - """Base class for a header defect.""" - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - -class InvalidHeaderDefect(HeaderDefect): - """Header is not valid, message gives details.""" - -class HeaderMissingRequiredValue(HeaderDefect): - """A header that must have a value had none""" - -class NonPrintableDefect(HeaderDefect): - """ASCII characters outside the ascii-printable range found""" - - def __init__(self, non_printables): - super().__init__(non_printables) - self.non_printables = non_printables - - def __str__(self): - return ("the following ASCII non-printables found in header: " - "{}".format(self.non_printables)) - -class ObsoleteHeaderDefect(HeaderDefect): - """Header uses syntax declared obsolete by RFC 5322""" - -class NonASCIILocalPartDefect(HeaderDefect): - """local_part contains non-ASCII characters""" - # This defect only occurs during unicode parsing, not when - # parsing messages decoded from binary. diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/feedparser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/feedparser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/feedparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/feedparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,525 +0,0 @@ -# Copyright (C) 2004-2006 Python Software Foundation -# Authors: Baxter, Wouters and Warsaw -# Contact: email-sig@python.org - -"""FeedParser - An email feed parser. - -The feed parser implements an interface for incrementally parsing an email -message, line by line. This has advantages for certain applications, such as -those reading email messages off a socket. - -FeedParser.feed() is the primary interface for pushing new data into the -parser. It returns when there's nothing more it can do with the available -data. When you have no more data to push into the parser, call .close(). -This completes the parsing and returns the root message object. - -The other advantage of this parser is that it will never raise a parsing -exception. Instead, when it finds something unexpected, it adds a 'defect' to -the current message. Defects are just instances that live on the message -object's .defects attribute. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import object, range, super -from future.utils import implements_iterator, PY3 - -__all__ = ['FeedParser', 'BytesFeedParser'] - -import re - -from future.backports.email import errors -from future.backports.email import message -from future.backports.email._policybase import compat32 - -NLCRE = re.compile('\r\n|\r|\n') -NLCRE_bol = re.compile('(\r\n|\r|\n)') -NLCRE_eol = re.compile('(\r\n|\r|\n)\Z') -NLCRE_crack = re.compile('(\r\n|\r|\n)') -# RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character -# except controls, SP, and ":". -headerRE = re.compile(r'^(From |[\041-\071\073-\176]{1,}:|[\t ])') -EMPTYSTRING = '' -NL = '\n' - -NeedMoreData = object() - - -# @implements_iterator -class BufferedSubFile(object): - """A file-ish object that can have new data loaded into it. - - You can also push and pop line-matching predicates onto a stack. When the - current predicate matches the current line, a false EOF response - (i.e. empty string) is returned instead. This lets the parser adhere to a - simple abstraction -- it parses until EOF closes the current message. - """ - def __init__(self): - # The last partial line pushed into this object. - self._partial = '' - # The list of full, pushed lines, in reverse order - self._lines = [] - # The stack of false-EOF checking predicates. - self._eofstack = [] - # A flag indicating whether the file has been closed or not. - self._closed = False - - def push_eof_matcher(self, pred): - self._eofstack.append(pred) - - def pop_eof_matcher(self): - return self._eofstack.pop() - - def close(self): - # Don't forget any trailing partial line. - self._lines.append(self._partial) - self._partial = '' - self._closed = True - - def readline(self): - if not self._lines: - if self._closed: - return '' - return NeedMoreData - # Pop the line off the stack and see if it matches the current - # false-EOF predicate. - line = self._lines.pop() - # RFC 2046, section 5.1.2 requires us to recognize outer level - # boundaries at any level of inner nesting. Do this, but be sure it's - # in the order of most to least nested. - for ateof in self._eofstack[::-1]: - if ateof(line): - # We're at the false EOF. But push the last line back first. - self._lines.append(line) - return '' - return line - - def unreadline(self, line): - # Let the consumer push a line back into the buffer. - assert line is not NeedMoreData - self._lines.append(line) - - def push(self, data): - """Push some new data into this object.""" - # Handle any previous leftovers - data, self._partial = self._partial + data, '' - # Crack into lines, but preserve the newlines on the end of each - parts = NLCRE_crack.split(data) - # The *ahem* interesting behaviour of re.split when supplied grouping - # parentheses is that the last element of the resulting list is the - # data after the final RE. In the case of a NL/CR terminated string, - # this is the empty string. - self._partial = parts.pop() - #GAN 29Mar09 bugs 1555570, 1721862 Confusion at 8K boundary ending with \r: - # is there a \n to follow later? - if not self._partial and parts and parts[-1].endswith('\r'): - self._partial = parts.pop(-2)+parts.pop() - # parts is a list of strings, alternating between the line contents - # and the eol character(s). Gather up a list of lines after - # re-attaching the newlines. - lines = [] - for i in range(len(parts) // 2): - lines.append(parts[i*2] + parts[i*2+1]) - self.pushlines(lines) - - def pushlines(self, lines): - # Reverse and insert at the front of the lines. - self._lines[:0] = lines[::-1] - - def __iter__(self): - return self - - def __next__(self): - line = self.readline() - if line == '': - raise StopIteration - return line - - -class FeedParser(object): - """A feed-style parser of email.""" - - def __init__(self, _factory=message.Message, **_3to2kwargs): - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = compat32 - """_factory is called with no arguments to create a new message obj - - The policy keyword specifies a policy object that controls a number of - aspects of the parser's operation. The default policy maintains - backward compatibility. - - """ - self._factory = _factory - self.policy = policy - try: - _factory(policy=self.policy) - self._factory_kwds = lambda: {'policy': self.policy} - except TypeError: - # Assume this is an old-style factory - self._factory_kwds = lambda: {} - self._input = BufferedSubFile() - self._msgstack = [] - if PY3: - self._parse = self._parsegen().__next__ - else: - self._parse = self._parsegen().next - self._cur = None - self._last = None - self._headersonly = False - - # Non-public interface for supporting Parser's headersonly flag - def _set_headersonly(self): - self._headersonly = True - - def feed(self, data): - """Push more data into the parser.""" - self._input.push(data) - self._call_parse() - - def _call_parse(self): - try: - self._parse() - except StopIteration: - pass - - def close(self): - """Parse all remaining data and return the root message object.""" - self._input.close() - self._call_parse() - root = self._pop_message() - assert not self._msgstack - # Look for final set of defects - if root.get_content_maintype() == 'multipart' \ - and not root.is_multipart(): - defect = errors.MultipartInvariantViolationDefect() - self.policy.handle_defect(root, defect) - return root - - def _new_message(self): - msg = self._factory(**self._factory_kwds()) - if self._cur and self._cur.get_content_type() == 'multipart/digest': - msg.set_default_type('message/rfc822') - if self._msgstack: - self._msgstack[-1].attach(msg) - self._msgstack.append(msg) - self._cur = msg - self._last = msg - - def _pop_message(self): - retval = self._msgstack.pop() - if self._msgstack: - self._cur = self._msgstack[-1] - else: - self._cur = None - return retval - - def _parsegen(self): - # Create a new message and start by parsing headers. - self._new_message() - headers = [] - # Collect the headers, searching for a line that doesn't match the RFC - # 2822 header or continuation pattern (including an empty line). - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - if not headerRE.match(line): - # If we saw the RFC defined header/body separator - # (i.e. newline), just throw it away. Otherwise the line is - # part of the body so push it back. - if not NLCRE.match(line): - defect = errors.MissingHeaderBodySeparatorDefect() - self.policy.handle_defect(self._cur, defect) - self._input.unreadline(line) - break - headers.append(line) - # Done with the headers, so parse them and figure out what we're - # supposed to see in the body of the message. - self._parse_headers(headers) - # Headers-only parsing is a backwards compatibility hack, which was - # necessary in the older parser, which could raise errors. All - # remaining lines in the input are thrown into the message body. - if self._headersonly: - lines = [] - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - if self._cur.get_content_type() == 'message/delivery-status': - # message/delivery-status contains blocks of headers separated by - # a blank line. We'll represent each header block as a separate - # nested message object, but the processing is a bit different - # than standard message/* types because there is no body for the - # nested messages. A blank line separates the subparts. - while True: - self._input.push_eof_matcher(NLCRE.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - msg = self._pop_message() - # We need to pop the EOF matcher in order to tell if we're at - # the end of the current file, not the end of the last block - # of message headers. - self._input.pop_eof_matcher() - # The input stream must be sitting at the newline or at the - # EOF. We want to see if we're at the end of this subpart, so - # first consume the blank line, then test the next line to see - # if we're at this subpart's EOF. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - if line == '': - break - # Not at EOF so this is a line we're going to need. - self._input.unreadline(line) - return - if self._cur.get_content_maintype() == 'message': - # The message claims to be a message/* type, then what follows is - # another RFC 2822 message. - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - self._pop_message() - return - if self._cur.get_content_maintype() == 'multipart': - boundary = self._cur.get_boundary() - if boundary is None: - # The message /claims/ to be a multipart but it has not - # defined a boundary. That's a problem which we'll handle by - # reading everything until the EOF and marking the message as - # defective. - defect = errors.NoBoundaryInMultipartDefect() - self.policy.handle_defect(self._cur, defect) - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - # Make sure a valid content type was specified per RFC 2045:6.4. - if (self._cur.get('content-transfer-encoding', '8bit').lower() - not in ('7bit', '8bit', 'binary')): - defect = errors.InvalidMultipartContentTransferEncodingDefect() - self.policy.handle_defect(self._cur, defect) - # Create a line match predicate which matches the inter-part - # boundary as well as the end-of-multipart boundary. Don't push - # this onto the input stream until we've scanned past the - # preamble. - separator = '--' + boundary - boundaryre = re.compile( - '(?P' + re.escape(separator) + - r')(?P--)?(?P[ \t]*)(?P\r\n|\r|\n)?$') - capturing_preamble = True - preamble = [] - linesep = False - close_boundary_seen = False - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - mo = boundaryre.match(line) - if mo: - # If we're looking at the end boundary, we're done with - # this multipart. If there was a newline at the end of - # the closing boundary, then we need to initialize the - # epilogue with the empty string (see below). - if mo.group('end'): - close_boundary_seen = True - linesep = mo.group('linesep') - break - # We saw an inter-part boundary. Were we in the preamble? - if capturing_preamble: - if preamble: - # According to RFC 2046, the last newline belongs - # to the boundary. - lastline = preamble[-1] - eolmo = NLCRE_eol.search(lastline) - if eolmo: - preamble[-1] = lastline[:-len(eolmo.group(0))] - self._cur.preamble = EMPTYSTRING.join(preamble) - capturing_preamble = False - self._input.unreadline(line) - continue - # We saw a boundary separating two parts. Consume any - # multiple boundary lines that may be following. Our - # interpretation of RFC 2046 BNF grammar does not produce - # body parts within such double boundaries. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - mo = boundaryre.match(line) - if not mo: - self._input.unreadline(line) - break - # Recurse to parse this subpart; the input stream points - # at the subpart's first line. - self._input.push_eof_matcher(boundaryre.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - # Because of RFC 2046, the newline preceding the boundary - # separator actually belongs to the boundary, not the - # previous subpart's payload (or epilogue if the previous - # part is a multipart). - if self._last.get_content_maintype() == 'multipart': - epilogue = self._last.epilogue - if epilogue == '': - self._last.epilogue = None - elif epilogue is not None: - mo = NLCRE_eol.search(epilogue) - if mo: - end = len(mo.group(0)) - self._last.epilogue = epilogue[:-end] - else: - payload = self._last._payload - if isinstance(payload, str): - mo = NLCRE_eol.search(payload) - if mo: - payload = payload[:-len(mo.group(0))] - self._last._payload = payload - self._input.pop_eof_matcher() - self._pop_message() - # Set the multipart up for newline cleansing, which will - # happen if we're in a nested multipart. - self._last = self._cur - else: - # I think we must be in the preamble - assert capturing_preamble - preamble.append(line) - # We've seen either the EOF or the end boundary. If we're still - # capturing the preamble, we never saw the start boundary. Note - # that as a defect and store the captured text as the payload. - if capturing_preamble: - defect = errors.StartBoundaryNotFoundDefect() - self.policy.handle_defect(self._cur, defect) - self._cur.set_payload(EMPTYSTRING.join(preamble)) - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # If we're not processing the preamble, then we might have seen - # EOF without seeing that end boundary...that is also a defect. - if not close_boundary_seen: - defect = errors.CloseBoundaryNotFoundDefect() - self.policy.handle_defect(self._cur, defect) - return - # Everything from here to the EOF is epilogue. If the end boundary - # ended in a newline, we'll need to make sure the epilogue isn't - # None - if linesep: - epilogue = [''] - else: - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - epilogue.append(line) - # Any CRLF at the front of the epilogue is not technically part of - # the epilogue. Also, watch out for an empty string epilogue, - # which means a single newline. - if epilogue: - firstline = epilogue[0] - bolmo = NLCRE_bol.match(firstline) - if bolmo: - epilogue[0] = firstline[len(bolmo.group(0)):] - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # Otherwise, it's some non-multipart type, so the entire rest of the - # file contents becomes the payload. - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - - def _parse_headers(self, lines): - # Passed a list of lines that make up the headers for the current msg - lastheader = '' - lastvalue = [] - for lineno, line in enumerate(lines): - # Check for continuation - if line[0] in ' \t': - if not lastheader: - # The first line of the headers was a continuation. This - # is illegal, so let's note the defect, store the illegal - # line, and ignore it for purposes of headers. - defect = errors.FirstHeaderLineIsContinuationDefect(line) - self.policy.handle_defect(self._cur, defect) - continue - lastvalue.append(line) - continue - if lastheader: - self._cur.set_raw(*self.policy.header_source_parse(lastvalue)) - lastheader, lastvalue = '', [] - # Check for envelope header, i.e. unix-from - if line.startswith('From '): - if lineno == 0: - # Strip off the trailing newline - mo = NLCRE_eol.search(line) - if mo: - line = line[:-len(mo.group(0))] - self._cur.set_unixfrom(line) - continue - elif lineno == len(lines) - 1: - # Something looking like a unix-from at the end - it's - # probably the first line of the body, so push back the - # line and stop. - self._input.unreadline(line) - return - else: - # Weirdly placed unix-from line. Note this as a defect - # and ignore it. - defect = errors.MisplacedEnvelopeHeaderDefect(line) - self._cur.defects.append(defect) - continue - # Split the line on the colon separating field name from value. - # There will always be a colon, because if there wasn't the part of - # the parser that calls us would have started parsing the body. - i = line.find(':') - assert i>0, "_parse_headers fed line with no : and no leading WS" - lastheader = line[:i] - lastvalue = [line] - # Done with all the lines, so handle the last header. - if lastheader: - self._cur.set_raw(*self.policy.header_source_parse(lastvalue)) - - -class BytesFeedParser(FeedParser): - """Like FeedParser, but feed accepts bytes.""" - - def feed(self, data): - super().feed(data.decode('ascii', 'surrogateescape')) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/generator.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/generator.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/generator.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/generator.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,498 +0,0 @@ -# Copyright (C) 2001-2010 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Classes to generate plain text from a message object tree.""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super -from future.builtins import str - -__all__ = ['Generator', 'DecodedGenerator', 'BytesGenerator'] - -import re -import sys -import time -import random -import warnings - -from io import StringIO, BytesIO -from future.backports.email._policybase import compat32 -from future.backports.email.header import Header -from future.backports.email.utils import _has_surrogates -import future.backports.email.charset as _charset - -UNDERSCORE = '_' -NL = '\n' # XXX: no longer used by the code below. - -fcre = re.compile(r'^From ', re.MULTILINE) - - -class Generator(object): - """Generates output from a Message object tree. - - This basic generator writes the message to the given file object as plain - text. - """ - # - # Public interface - # - - def __init__(self, outfp, mangle_from_=True, maxheaderlen=None, **_3to2kwargs): - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = None - """Create the generator for message flattening. - - outfp is the output file-like object for writing the message to. It - must have a write() method. - - Optional mangle_from_ is a flag that, when True (the default), escapes - From_ lines in the body of the message by putting a `>' in front of - them. - - Optional maxheaderlen specifies the longest length for a non-continued - header. When a header line is longer (in characters, with tabs - expanded to 8 spaces) than maxheaderlen, the header will split as - defined in the Header class. Set maxheaderlen to zero to disable - header wrapping. The default is 78, as recommended (but not required) - by RFC 2822. - - The policy keyword specifies a policy object that controls a number of - aspects of the generator's operation. The default policy maintains - backward compatibility. - - """ - self._fp = outfp - self._mangle_from_ = mangle_from_ - self.maxheaderlen = maxheaderlen - self.policy = policy - - def write(self, s): - # Just delegate to the file object - self._fp.write(s) - - def flatten(self, msg, unixfrom=False, linesep=None): - r"""Print the message object tree rooted at msg to the output file - specified when the Generator instance was created. - - unixfrom is a flag that forces the printing of a Unix From_ delimiter - before the first object in the message tree. If the original message - has no From_ delimiter, a `standard' one is crafted. By default, this - is False to inhibit the printing of any From_ delimiter. - - Note that for subobjects, no From_ line is printed. - - linesep specifies the characters used to indicate a new line in - the output. The default value is determined by the policy. - - """ - # We use the _XXX constants for operating on data that comes directly - # from the msg, and _encoded_XXX constants for operating on data that - # has already been converted (to bytes in the BytesGenerator) and - # inserted into a temporary buffer. - policy = msg.policy if self.policy is None else self.policy - if linesep is not None: - policy = policy.clone(linesep=linesep) - if self.maxheaderlen is not None: - policy = policy.clone(max_line_length=self.maxheaderlen) - self._NL = policy.linesep - self._encoded_NL = self._encode(self._NL) - self._EMPTY = '' - self._encoded_EMTPY = self._encode('') - # Because we use clone (below) when we recursively process message - # subparts, and because clone uses the computed policy (not None), - # submessages will automatically get set to the computed policy when - # they are processed by this code. - old_gen_policy = self.policy - old_msg_policy = msg.policy - try: - self.policy = policy - msg.policy = policy - if unixfrom: - ufrom = msg.get_unixfrom() - if not ufrom: - ufrom = 'From nobody ' + time.ctime(time.time()) - self.write(ufrom + self._NL) - self._write(msg) - finally: - self.policy = old_gen_policy - msg.policy = old_msg_policy - - def clone(self, fp): - """Clone this generator with the exact same options.""" - return self.__class__(fp, - self._mangle_from_, - None, # Use policy setting, which we've adjusted - policy=self.policy) - - # - # Protected interface - undocumented ;/ - # - - # Note that we use 'self.write' when what we are writing is coming from - # the source, and self._fp.write when what we are writing is coming from a - # buffer (because the Bytes subclass has already had a chance to transform - # the data in its write method in that case). This is an entirely - # pragmatic split determined by experiment; we could be more general by - # always using write and having the Bytes subclass write method detect when - # it has already transformed the input; but, since this whole thing is a - # hack anyway this seems good enough. - - # Similarly, we have _XXX and _encoded_XXX attributes that are used on - # source and buffer data, respectively. - _encoded_EMPTY = '' - - def _new_buffer(self): - # BytesGenerator overrides this to return BytesIO. - return StringIO() - - def _encode(self, s): - # BytesGenerator overrides this to encode strings to bytes. - return s - - def _write_lines(self, lines): - # We have to transform the line endings. - if not lines: - return - lines = lines.splitlines(True) - for line in lines[:-1]: - self.write(line.rstrip('\r\n')) - self.write(self._NL) - laststripped = lines[-1].rstrip('\r\n') - self.write(laststripped) - if len(lines[-1]) != len(laststripped): - self.write(self._NL) - - def _write(self, msg): - # We can't write the headers yet because of the following scenario: - # say a multipart message includes the boundary string somewhere in - # its body. We'd have to calculate the new boundary /before/ we write - # the headers so that we can write the correct Content-Type: - # parameter. - # - # The way we do this, so as to make the _handle_*() methods simpler, - # is to cache any subpart writes into a buffer. The we write the - # headers and the buffer contents. That way, subpart handlers can - # Do The Right Thing, and can still modify the Content-Type: header if - # necessary. - oldfp = self._fp - try: - self._fp = sfp = self._new_buffer() - self._dispatch(msg) - finally: - self._fp = oldfp - # Write the headers. First we see if the message object wants to - # handle that itself. If not, we'll do it generically. - meth = getattr(msg, '_write_headers', None) - if meth is None: - self._write_headers(msg) - else: - meth(self) - self._fp.write(sfp.getvalue()) - - def _dispatch(self, msg): - # Get the Content-Type: for the message, then try to dispatch to - # self._handle__(). If there's no handler for the - # full MIME type, then dispatch to self._handle_(). If - # that's missing too, then dispatch to self._writeBody(). - main = msg.get_content_maintype() - sub = msg.get_content_subtype() - specific = UNDERSCORE.join((main, sub)).replace('-', '_') - meth = getattr(self, '_handle_' + specific, None) - if meth is None: - generic = main.replace('-', '_') - meth = getattr(self, '_handle_' + generic, None) - if meth is None: - meth = self._writeBody - meth(msg) - - # - # Default handlers - # - - def _write_headers(self, msg): - for h, v in msg.raw_items(): - self.write(self.policy.fold(h, v)) - # A blank line always separates headers from body - self.write(self._NL) - - # - # Handlers for writing types and subtypes - # - - def _handle_text(self, msg): - payload = msg.get_payload() - if payload is None: - return - if not isinstance(payload, str): - raise TypeError('string payload expected: %s' % type(payload)) - if _has_surrogates(msg._payload): - charset = msg.get_param('charset') - if charset is not None: - del msg['content-transfer-encoding'] - msg.set_payload(payload, charset) - payload = msg.get_payload() - if self._mangle_from_: - payload = fcre.sub('>From ', payload) - self._write_lines(payload) - - # Default body handler - _writeBody = _handle_text - - def _handle_multipart(self, msg): - # The trick here is to write out each part separately, merge them all - # together, and then make sure that the boundary we've chosen isn't - # present in the payload. - msgtexts = [] - subparts = msg.get_payload() - if subparts is None: - subparts = [] - elif isinstance(subparts, str): - # e.g. a non-strict parse of a message with no starting boundary. - self.write(subparts) - return - elif not isinstance(subparts, list): - # Scalar payload - subparts = [subparts] - for part in subparts: - s = self._new_buffer() - g = self.clone(s) - g.flatten(part, unixfrom=False, linesep=self._NL) - msgtexts.append(s.getvalue()) - # BAW: What about boundaries that are wrapped in double-quotes? - boundary = msg.get_boundary() - if not boundary: - # Create a boundary that doesn't appear in any of the - # message texts. - alltext = self._encoded_NL.join(msgtexts) - boundary = self._make_boundary(alltext) - msg.set_boundary(boundary) - # If there's a preamble, write it out, with a trailing CRLF - if msg.preamble is not None: - if self._mangle_from_: - preamble = fcre.sub('>From ', msg.preamble) - else: - preamble = msg.preamble - self._write_lines(preamble) - self.write(self._NL) - # dash-boundary transport-padding CRLF - self.write('--' + boundary + self._NL) - # body-part - if msgtexts: - self._fp.write(msgtexts.pop(0)) - # *encapsulation - # --> delimiter transport-padding - # --> CRLF body-part - for body_part in msgtexts: - # delimiter transport-padding CRLF - self.write(self._NL + '--' + boundary + self._NL) - # body-part - self._fp.write(body_part) - # close-delimiter transport-padding - self.write(self._NL + '--' + boundary + '--') - if msg.epilogue is not None: - self.write(self._NL) - if self._mangle_from_: - epilogue = fcre.sub('>From ', msg.epilogue) - else: - epilogue = msg.epilogue - self._write_lines(epilogue) - - def _handle_multipart_signed(self, msg): - # The contents of signed parts has to stay unmodified in order to keep - # the signature intact per RFC1847 2.1, so we disable header wrapping. - # RDM: This isn't enough to completely preserve the part, but it helps. - p = self.policy - self.policy = p.clone(max_line_length=0) - try: - self._handle_multipart(msg) - finally: - self.policy = p - - def _handle_message_delivery_status(self, msg): - # We can't just write the headers directly to self's file object - # because this will leave an extra newline between the last header - # block and the boundary. Sigh. - blocks = [] - for part in msg.get_payload(): - s = self._new_buffer() - g = self.clone(s) - g.flatten(part, unixfrom=False, linesep=self._NL) - text = s.getvalue() - lines = text.split(self._encoded_NL) - # Strip off the unnecessary trailing empty line - if lines and lines[-1] == self._encoded_EMPTY: - blocks.append(self._encoded_NL.join(lines[:-1])) - else: - blocks.append(text) - # Now join all the blocks with an empty line. This has the lovely - # effect of separating each block with an empty line, but not adding - # an extra one after the last one. - self._fp.write(self._encoded_NL.join(blocks)) - - def _handle_message(self, msg): - s = self._new_buffer() - g = self.clone(s) - # The payload of a message/rfc822 part should be a multipart sequence - # of length 1. The zeroth element of the list should be the Message - # object for the subpart. Extract that object, stringify it, and - # write it out. - # Except, it turns out, when it's a string instead, which happens when - # and only when HeaderParser is used on a message of mime type - # message/rfc822. Such messages are generated by, for example, - # Groupwise when forwarding unadorned messages. (Issue 7970.) So - # in that case we just emit the string body. - payload = msg._payload - if isinstance(payload, list): - g.flatten(msg.get_payload(0), unixfrom=False, linesep=self._NL) - payload = s.getvalue() - else: - payload = self._encode(payload) - self._fp.write(payload) - - # This used to be a module level function; we use a classmethod for this - # and _compile_re so we can continue to provide the module level function - # for backward compatibility by doing - # _make_boudary = Generator._make_boundary - # at the end of the module. It *is* internal, so we could drop that... - @classmethod - def _make_boundary(cls, text=None): - # Craft a random boundary. If text is given, ensure that the chosen - # boundary doesn't appear in the text. - token = random.randrange(sys.maxsize) - boundary = ('=' * 15) + (_fmt % token) + '==' - if text is None: - return boundary - b = boundary - counter = 0 - while True: - cre = cls._compile_re('^--' + re.escape(b) + '(--)?$', re.MULTILINE) - if not cre.search(text): - break - b = boundary + '.' + str(counter) - counter += 1 - return b - - @classmethod - def _compile_re(cls, s, flags): - return re.compile(s, flags) - -class BytesGenerator(Generator): - """Generates a bytes version of a Message object tree. - - Functionally identical to the base Generator except that the output is - bytes and not string. When surrogates were used in the input to encode - bytes, these are decoded back to bytes for output. If the policy has - cte_type set to 7bit, then the message is transformed such that the - non-ASCII bytes are properly content transfer encoded, using the charset - unknown-8bit. - - The outfp object must accept bytes in its write method. - """ - - # Bytes versions of this constant for use in manipulating data from - # the BytesIO buffer. - _encoded_EMPTY = b'' - - def write(self, s): - self._fp.write(str(s).encode('ascii', 'surrogateescape')) - - def _new_buffer(self): - return BytesIO() - - def _encode(self, s): - return s.encode('ascii') - - def _write_headers(self, msg): - # This is almost the same as the string version, except for handling - # strings with 8bit bytes. - for h, v in msg.raw_items(): - self._fp.write(self.policy.fold_binary(h, v)) - # A blank line always separates headers from body - self.write(self._NL) - - def _handle_text(self, msg): - # If the string has surrogates the original source was bytes, so - # just write it back out. - if msg._payload is None: - return - if _has_surrogates(msg._payload) and not self.policy.cte_type=='7bit': - if self._mangle_from_: - msg._payload = fcre.sub(">From ", msg._payload) - self._write_lines(msg._payload) - else: - super(BytesGenerator,self)._handle_text(msg) - - # Default body handler - _writeBody = _handle_text - - @classmethod - def _compile_re(cls, s, flags): - return re.compile(s.encode('ascii'), flags) - - -_FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' - -class DecodedGenerator(Generator): - """Generates a text representation of a message. - - Like the Generator base class, except that non-text parts are substituted - with a format string representing the part. - """ - def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None): - """Like Generator.__init__() except that an additional optional - argument is allowed. - - Walks through all subparts of a message. If the subpart is of main - type `text', then it prints the decoded payload of the subpart. - - Otherwise, fmt is a format string that is used instead of the message - payload. fmt is expanded with the following keywords (in - %(keyword)s format): - - type : Full MIME type of the non-text part - maintype : Main MIME type of the non-text part - subtype : Sub-MIME type of the non-text part - filename : Filename of the non-text part - description: Description associated with the non-text part - encoding : Content transfer encoding of the non-text part - - The default value for fmt is None, meaning - - [Non-text (%(type)s) part of message omitted, filename %(filename)s] - """ - Generator.__init__(self, outfp, mangle_from_, maxheaderlen) - if fmt is None: - self._fmt = _FMT - else: - self._fmt = fmt - - def _dispatch(self, msg): - for part in msg.walk(): - maintype = part.get_content_maintype() - if maintype == 'text': - print(part.get_payload(decode=False), file=self) - elif maintype == 'multipart': - # Just skip this - pass - else: - print(self._fmt % { - 'type' : part.get_content_type(), - 'maintype' : part.get_content_maintype(), - 'subtype' : part.get_content_subtype(), - 'filename' : part.get_filename('[no filename]'), - 'description': part.get('Content-Description', - '[no description]'), - 'encoding' : part.get('Content-Transfer-Encoding', - '[no encoding]'), - }, file=self) - - -# Helper used by Generator._make_boundary -_width = len(repr(sys.maxsize-1)) -_fmt = '%%0%dd' % _width - -# Backward compatibility -_make_boundary = Generator._make_boundary diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/header.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/header.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/header.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/header.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,581 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -"""Header encoding and decoding functionality.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes, range, str, super, zip - -__all__ = [ - 'Header', - 'decode_header', - 'make_header', - ] - -import re -import binascii - -from future.backports import email -from future.backports.email import base64mime -from future.backports.email.errors import HeaderParseError -import future.backports.email.charset as _charset - -# Helpers -from future.backports.email.quoprimime import _max_append, header_decode - -Charset = _charset.Charset - -NL = '\n' -SPACE = ' ' -BSPACE = b' ' -SPACE8 = ' ' * 8 -EMPTYSTRING = '' -MAXLINELEN = 78 -FWS = ' \t' - -USASCII = Charset('us-ascii') -UTF8 = Charset('utf-8') - -# Match encoded-word strings in the form =?charset?q?Hello_World?= -ecre = re.compile(r''' - =\? # literal =? - (?P[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P.*?) # non-greedy up to the next ?= is the encoded string - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE | re.MULTILINE) - -# Field name regexp, including trailing colon, but not separating whitespace, -# according to RFC 2822. Character range is from tilde to exclamation mark. -# For use with .match() -fcre = re.compile(r'[\041-\176]+:$') - -# Find a header embedded in a putative header value. Used to check for -# header injection attack. -_embeded_header = re.compile(r'\n[^ \t]+:') - - -def decode_header(header): - """Decode a message header value without converting charset. - - Returns a list of (string, charset) pairs containing each of the decoded - parts of the header. Charset is None for non-encoded parts of the header, - otherwise a lower-case string containing the name of the character set - specified in the encoded string. - - header may be a string that may or may not contain RFC2047 encoded words, - or it may be a Header object. - - An email.errors.HeaderParseError may be raised when certain decoding error - occurs (e.g. a base64 decoding exception). - """ - # If it is a Header object, we can just return the encoded chunks. - if hasattr(header, '_chunks'): - return [(_charset._encode(string, str(charset)), str(charset)) - for string, charset in header._chunks] - # If no encoding, just return the header with no charset. - if not ecre.search(header): - return [(header, None)] - # First step is to parse all the encoded parts into triplets of the form - # (encoded_string, encoding, charset). For unencoded strings, the last - # two parts will be None. - words = [] - for line in header.splitlines(): - parts = ecre.split(line) - first = True - while parts: - unencoded = parts.pop(0) - if first: - unencoded = unencoded.lstrip() - first = False - if unencoded: - words.append((unencoded, None, None)) - if parts: - charset = parts.pop(0).lower() - encoding = parts.pop(0).lower() - encoded = parts.pop(0) - words.append((encoded, encoding, charset)) - # Now loop over words and remove words that consist of whitespace - # between two encoded strings. - import sys - droplist = [] - for n, w in enumerate(words): - if n>1 and w[1] and words[n-2][1] and words[n-1][0].isspace(): - droplist.append(n-1) - for d in reversed(droplist): - del words[d] - - # The next step is to decode each encoded word by applying the reverse - # base64 or quopri transformation. decoded_words is now a list of the - # form (decoded_word, charset). - decoded_words = [] - for encoded_string, encoding, charset in words: - if encoding is None: - # This is an unencoded word. - decoded_words.append((encoded_string, charset)) - elif encoding == 'q': - word = header_decode(encoded_string) - decoded_words.append((word, charset)) - elif encoding == 'b': - paderr = len(encoded_string) % 4 # Postel's law: add missing padding - if paderr: - encoded_string += '==='[:4 - paderr] - try: - word = base64mime.decode(encoded_string) - except binascii.Error: - raise HeaderParseError('Base64 decoding error') - else: - decoded_words.append((word, charset)) - else: - raise AssertionError('Unexpected encoding: ' + encoding) - # Now convert all words to bytes and collapse consecutive runs of - # similarly encoded words. - collapsed = [] - last_word = last_charset = None - for word, charset in decoded_words: - if isinstance(word, str): - word = bytes(word, 'raw-unicode-escape') - if last_word is None: - last_word = word - last_charset = charset - elif charset != last_charset: - collapsed.append((last_word, last_charset)) - last_word = word - last_charset = charset - elif last_charset is None: - last_word += BSPACE + word - else: - last_word += word - collapsed.append((last_word, last_charset)) - return collapsed - - -def make_header(decoded_seq, maxlinelen=None, header_name=None, - continuation_ws=' '): - """Create a Header from a sequence of pairs as returned by decode_header() - - decode_header() takes a header value string and returns a sequence of - pairs of the format (decoded_string, charset) where charset is the string - name of the character set. - - This function takes one of those sequence of pairs and returns a Header - instance. Optional maxlinelen, header_name, and continuation_ws are as in - the Header constructor. - """ - h = Header(maxlinelen=maxlinelen, header_name=header_name, - continuation_ws=continuation_ws) - for s, charset in decoded_seq: - # None means us-ascii but we can simply pass it on to h.append() - if charset is not None and not isinstance(charset, Charset): - charset = Charset(charset) - h.append(s, charset) - return h - - -class Header(object): - def __init__(self, s=None, charset=None, - maxlinelen=None, header_name=None, - continuation_ws=' ', errors='strict'): - """Create a MIME-compliant header that can contain many character sets. - - Optional s is the initial header value. If None, the initial header - value is not set. You can later append to the header with .append() - method calls. s may be a byte string or a Unicode string, but see the - .append() documentation for semantics. - - Optional charset serves two purposes: it has the same meaning as the - charset argument to the .append() method. It also sets the default - character set for all subsequent .append() calls that omit the charset - argument. If charset is not provided in the constructor, the us-ascii - charset is used both as s's initial charset and as the default for - subsequent .append() calls. - - The maximum line length can be specified explicitly via maxlinelen. For - splitting the first line to a shorter value (to account for the field - header which isn't included in s, e.g. `Subject') pass in the name of - the field in header_name. The default maxlinelen is 78 as recommended - by RFC 2822. - - continuation_ws must be RFC 2822 compliant folding whitespace (usually - either a space or a hard tab) which will be prepended to continuation - lines. - - errors is passed through to the .append() call. - """ - if charset is None: - charset = USASCII - elif not isinstance(charset, Charset): - charset = Charset(charset) - self._charset = charset - self._continuation_ws = continuation_ws - self._chunks = [] - if s is not None: - self.append(s, charset, errors) - if maxlinelen is None: - maxlinelen = MAXLINELEN - self._maxlinelen = maxlinelen - if header_name is None: - self._headerlen = 0 - else: - # Take the separating colon and space into account. - self._headerlen = len(header_name) + 2 - - def __str__(self): - """Return the string value of the header.""" - self._normalize() - uchunks = [] - lastcs = None - lastspace = None - for string, charset in self._chunks: - # We must preserve spaces between encoded and non-encoded word - # boundaries, which means for us we need to add a space when we go - # from a charset to None/us-ascii, or from None/us-ascii to a - # charset. Only do this for the second and subsequent chunks. - # Don't add a space if the None/us-ascii string already has - # a space (trailing or leading depending on transition) - nextcs = charset - if nextcs == _charset.UNKNOWN8BIT: - original_bytes = string.encode('ascii', 'surrogateescape') - string = original_bytes.decode('ascii', 'replace') - if uchunks: - hasspace = string and self._nonctext(string[0]) - if lastcs not in (None, 'us-ascii'): - if nextcs in (None, 'us-ascii') and not hasspace: - uchunks.append(SPACE) - nextcs = None - elif nextcs not in (None, 'us-ascii') and not lastspace: - uchunks.append(SPACE) - lastspace = string and self._nonctext(string[-1]) - lastcs = nextcs - uchunks.append(string) - return EMPTYSTRING.join(uchunks) - - # Rich comparison operators for equality only. BAW: does it make sense to - # have or explicitly disable <, <=, >, >= operators? - def __eq__(self, other): - # other may be a Header or a string. Both are fine so coerce - # ourselves to a unicode (of the unencoded header value), swap the - # args and do another comparison. - return other == str(self) - - def __ne__(self, other): - return not self == other - - def append(self, s, charset=None, errors='strict'): - """Append a string to the MIME header. - - Optional charset, if given, should be a Charset instance or the name - of a character set (which will be converted to a Charset instance). A - value of None (the default) means that the charset given in the - constructor is used. - - s may be a byte string or a Unicode string. If it is a byte string - (i.e. isinstance(s, str) is false), then charset is the encoding of - that byte string, and a UnicodeError will be raised if the string - cannot be decoded with that charset. If s is a Unicode string, then - charset is a hint specifying the character set of the characters in - the string. In either case, when producing an RFC 2822 compliant - header using RFC 2047 rules, the string will be encoded using the - output codec of the charset. If the string cannot be encoded to the - output codec, a UnicodeError will be raised. - - Optional `errors' is passed as the errors argument to the decode - call if s is a byte string. - """ - if charset is None: - charset = self._charset - elif not isinstance(charset, Charset): - charset = Charset(charset) - if not isinstance(s, str): - input_charset = charset.input_codec or 'us-ascii' - if input_charset == _charset.UNKNOWN8BIT: - s = s.decode('us-ascii', 'surrogateescape') - else: - s = s.decode(input_charset, errors) - # Ensure that the bytes we're storing can be decoded to the output - # character set, otherwise an early error is raised. - output_charset = charset.output_codec or 'us-ascii' - if output_charset != _charset.UNKNOWN8BIT: - try: - s.encode(output_charset, errors) - except UnicodeEncodeError: - if output_charset!='us-ascii': - raise - charset = UTF8 - self._chunks.append((s, charset)) - - def _nonctext(self, s): - """True if string s is not a ctext character of RFC822. - """ - return s.isspace() or s in ('(', ')', '\\') - - def encode(self, splitchars=';, \t', maxlinelen=None, linesep='\n'): - r"""Encode a message header into an RFC-compliant format. - - There are many issues involved in converting a given string for use in - an email header. Only certain character sets are readable in most - email clients, and as header strings can only contain a subset of - 7-bit ASCII, care must be taken to properly convert and encode (with - Base64 or quoted-printable) header strings. In addition, there is a - 75-character length limit on any given encoded header field, so - line-wrapping must be performed, even with double-byte character sets. - - Optional maxlinelen specifies the maximum length of each generated - line, exclusive of the linesep string. Individual lines may be longer - than maxlinelen if a folding point cannot be found. The first line - will be shorter by the length of the header name plus ": " if a header - name was specified at Header construction time. The default value for - maxlinelen is determined at header construction time. - - Optional splitchars is a string containing characters which should be - given extra weight by the splitting algorithm during normal header - wrapping. This is in very rough support of RFC 2822's `higher level - syntactic breaks': split points preceded by a splitchar are preferred - during line splitting, with the characters preferred in the order in - which they appear in the string. Space and tab may be included in the - string to indicate whether preference should be given to one over the - other as a split point when other split chars do not appear in the line - being split. Splitchars does not affect RFC 2047 encoded lines. - - Optional linesep is a string to be used to separate the lines of - the value. The default value is the most useful for typical - Python applications, but it can be set to \r\n to produce RFC-compliant - line separators when needed. - """ - self._normalize() - if maxlinelen is None: - maxlinelen = self._maxlinelen - # A maxlinelen of 0 means don't wrap. For all practical purposes, - # choosing a huge number here accomplishes that and makes the - # _ValueFormatter algorithm much simpler. - if maxlinelen == 0: - maxlinelen = 1000000 - formatter = _ValueFormatter(self._headerlen, maxlinelen, - self._continuation_ws, splitchars) - lastcs = None - hasspace = lastspace = None - for string, charset in self._chunks: - if hasspace is not None: - hasspace = string and self._nonctext(string[0]) - import sys - if lastcs not in (None, 'us-ascii'): - if not hasspace or charset not in (None, 'us-ascii'): - formatter.add_transition() - elif charset not in (None, 'us-ascii') and not lastspace: - formatter.add_transition() - lastspace = string and self._nonctext(string[-1]) - lastcs = charset - hasspace = False - lines = string.splitlines() - if lines: - formatter.feed('', lines[0], charset) - else: - formatter.feed('', '', charset) - for line in lines[1:]: - formatter.newline() - if charset.header_encoding is not None: - formatter.feed(self._continuation_ws, ' ' + line.lstrip(), - charset) - else: - sline = line.lstrip() - fws = line[:len(line)-len(sline)] - formatter.feed(fws, sline, charset) - if len(lines) > 1: - formatter.newline() - if self._chunks: - formatter.add_transition() - value = formatter._str(linesep) - if _embeded_header.search(value): - raise HeaderParseError("header value appears to contain " - "an embedded header: {!r}".format(value)) - return value - - def _normalize(self): - # Step 1: Normalize the chunks so that all runs of identical charsets - # get collapsed into a single unicode string. - chunks = [] - last_charset = None - last_chunk = [] - for string, charset in self._chunks: - if charset == last_charset: - last_chunk.append(string) - else: - if last_charset is not None: - chunks.append((SPACE.join(last_chunk), last_charset)) - last_chunk = [string] - last_charset = charset - if last_chunk: - chunks.append((SPACE.join(last_chunk), last_charset)) - self._chunks = chunks - - -class _ValueFormatter(object): - def __init__(self, headerlen, maxlen, continuation_ws, splitchars): - self._maxlen = maxlen - self._continuation_ws = continuation_ws - self._continuation_ws_len = len(continuation_ws) - self._splitchars = splitchars - self._lines = [] - self._current_line = _Accumulator(headerlen) - - def _str(self, linesep): - self.newline() - return linesep.join(self._lines) - - def __str__(self): - return self._str(NL) - - def newline(self): - end_of_line = self._current_line.pop() - if end_of_line != (' ', ''): - self._current_line.push(*end_of_line) - if len(self._current_line) > 0: - if self._current_line.is_onlyws(): - self._lines[-1] += str(self._current_line) - else: - self._lines.append(str(self._current_line)) - self._current_line.reset() - - def add_transition(self): - self._current_line.push(' ', '') - - def feed(self, fws, string, charset): - # If the charset has no header encoding (i.e. it is an ASCII encoding) - # then we must split the header at the "highest level syntactic break" - # possible. Note that we don't have a lot of smarts about field - # syntax; we just try to break on semi-colons, then commas, then - # whitespace. Eventually, this should be pluggable. - if charset.header_encoding is None: - self._ascii_split(fws, string, self._splitchars) - return - # Otherwise, we're doing either a Base64 or a quoted-printable - # encoding which means we don't need to split the line on syntactic - # breaks. We can basically just find enough characters to fit on the - # current line, minus the RFC 2047 chrome. What makes this trickier - # though is that we have to split at octet boundaries, not character - # boundaries but it's only safe to split at character boundaries so at - # best we can only get close. - encoded_lines = charset.header_encode_lines(string, self._maxlengths()) - # The first element extends the current line, but if it's None then - # nothing more fit on the current line so start a new line. - try: - first_line = encoded_lines.pop(0) - except IndexError: - # There are no encoded lines, so we're done. - return - if first_line is not None: - self._append_chunk(fws, first_line) - try: - last_line = encoded_lines.pop() - except IndexError: - # There was only one line. - return - self.newline() - self._current_line.push(self._continuation_ws, last_line) - # Everything else are full lines in themselves. - for line in encoded_lines: - self._lines.append(self._continuation_ws + line) - - def _maxlengths(self): - # The first line's length. - yield self._maxlen - len(self._current_line) - while True: - yield self._maxlen - self._continuation_ws_len - - def _ascii_split(self, fws, string, splitchars): - # The RFC 2822 header folding algorithm is simple in principle but - # complex in practice. Lines may be folded any place where "folding - # white space" appears by inserting a linesep character in front of the - # FWS. The complication is that not all spaces or tabs qualify as FWS, - # and we are also supposed to prefer to break at "higher level - # syntactic breaks". We can't do either of these without intimate - # knowledge of the structure of structured headers, which we don't have - # here. So the best we can do here is prefer to break at the specified - # splitchars, and hope that we don't choose any spaces or tabs that - # aren't legal FWS. (This is at least better than the old algorithm, - # where we would sometimes *introduce* FWS after a splitchar, or the - # algorithm before that, where we would turn all white space runs into - # single spaces or tabs.) - parts = re.split("(["+FWS+"]+)", fws+string) - if parts[0]: - parts[:0] = [''] - else: - parts.pop(0) - for fws, part in zip(*[iter(parts)]*2): - self._append_chunk(fws, part) - - def _append_chunk(self, fws, string): - self._current_line.push(fws, string) - if len(self._current_line) > self._maxlen: - # Find the best split point, working backward from the end. - # There might be none, on a long first line. - for ch in self._splitchars: - for i in range(self._current_line.part_count()-1, 0, -1): - if ch.isspace(): - fws = self._current_line[i][0] - if fws and fws[0]==ch: - break - prevpart = self._current_line[i-1][1] - if prevpart and prevpart[-1]==ch: - break - else: - continue - break - else: - fws, part = self._current_line.pop() - if self._current_line._initial_size > 0: - # There will be a header, so leave it on a line by itself. - self.newline() - if not fws: - # We don't use continuation_ws here because the whitespace - # after a header should always be a space. - fws = ' ' - self._current_line.push(fws, part) - return - remainder = self._current_line.pop_from(i) - self._lines.append(str(self._current_line)) - self._current_line.reset(remainder) - - -class _Accumulator(list): - - def __init__(self, initial_size=0): - self._initial_size = initial_size - super().__init__() - - def push(self, fws, string): - self.append((fws, string)) - - def pop_from(self, i=0): - popped = self[i:] - self[i:] = [] - return popped - - def pop(self): - if self.part_count()==0: - return ('', '') - return super().pop() - - def __len__(self): - return sum((len(fws)+len(part) for fws, part in self), - self._initial_size) - - def __str__(self): - return EMPTYSTRING.join((EMPTYSTRING.join((fws, part)) - for fws, part in self)) - - def reset(self, startval=None): - if startval is None: - startval = [] - self[:] = startval - self._initial_size = 0 - - def is_onlyws(self): - return self._initial_size==0 and (not self or str(self).isspace()) - - def part_count(self): - return super().__len__() diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/headerregistry.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/headerregistry.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/headerregistry.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/headerregistry.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,592 +0,0 @@ -"""Representing and manipulating email headers via custom objects. - -This module provides an implementation of the HeaderRegistry API. -The implementation is designed to flexibly follow RFC5322 rules. - -Eventually HeaderRegistry will be a public API, but it isn't yet, -and will probably change some before that happens. - -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -from future.builtins import super -from future.builtins import str -from future.utils import text_to_native_str -from future.backports.email import utils -from future.backports.email import errors -from future.backports.email import _header_value_parser as parser - -class Address(object): - - def __init__(self, display_name='', username='', domain='', addr_spec=None): - """Create an object represeting a full email address. - - An address can have a 'display_name', a 'username', and a 'domain'. In - addition to specifying the username and domain separately, they may be - specified together by using the addr_spec keyword *instead of* the - username and domain keywords. If an addr_spec string is specified it - must be properly quoted according to RFC 5322 rules; an error will be - raised if it is not. - - An Address object has display_name, username, domain, and addr_spec - attributes, all of which are read-only. The addr_spec and the string - value of the object are both quoted according to RFC5322 rules, but - without any Content Transfer Encoding. - - """ - # This clause with its potential 'raise' may only happen when an - # application program creates an Address object using an addr_spec - # keyword. The email library code itself must always supply username - # and domain. - if addr_spec is not None: - if username or domain: - raise TypeError("addrspec specified when username and/or " - "domain also specified") - a_s, rest = parser.get_addr_spec(addr_spec) - if rest: - raise ValueError("Invalid addr_spec; only '{}' " - "could be parsed from '{}'".format( - a_s, addr_spec)) - if a_s.all_defects: - raise a_s.all_defects[0] - username = a_s.local_part - domain = a_s.domain - self._display_name = display_name - self._username = username - self._domain = domain - - @property - def display_name(self): - return self._display_name - - @property - def username(self): - return self._username - - @property - def domain(self): - return self._domain - - @property - def addr_spec(self): - """The addr_spec (username@domain) portion of the address, quoted - according to RFC 5322 rules, but with no Content Transfer Encoding. - """ - nameset = set(self.username) - if len(nameset) > len(nameset-parser.DOT_ATOM_ENDS): - lp = parser.quote_string(self.username) - else: - lp = self.username - if self.domain: - return lp + '@' + self.domain - if not lp: - return '<>' - return lp - - def __repr__(self): - return "Address(display_name={!r}, username={!r}, domain={!r})".format( - self.display_name, self.username, self.domain) - - def __str__(self): - nameset = set(self.display_name) - if len(nameset) > len(nameset-parser.SPECIALS): - disp = parser.quote_string(self.display_name) - else: - disp = self.display_name - if disp: - addr_spec = '' if self.addr_spec=='<>' else self.addr_spec - return "{} <{}>".format(disp, addr_spec) - return self.addr_spec - - def __eq__(self, other): - if type(other) != type(self): - return False - return (self.display_name == other.display_name and - self.username == other.username and - self.domain == other.domain) - - -class Group(object): - - def __init__(self, display_name=None, addresses=None): - """Create an object representing an address group. - - An address group consists of a display_name followed by colon and an - list of addresses (see Address) terminated by a semi-colon. The Group - is created by specifying a display_name and a possibly empty list of - Address objects. A Group can also be used to represent a single - address that is not in a group, which is convenient when manipulating - lists that are a combination of Groups and individual Addresses. In - this case the display_name should be set to None. In particular, the - string representation of a Group whose display_name is None is the same - as the Address object, if there is one and only one Address object in - the addresses list. - - """ - self._display_name = display_name - self._addresses = tuple(addresses) if addresses else tuple() - - @property - def display_name(self): - return self._display_name - - @property - def addresses(self): - return self._addresses - - def __repr__(self): - return "Group(display_name={!r}, addresses={!r}".format( - self.display_name, self.addresses) - - def __str__(self): - if self.display_name is None and len(self.addresses)==1: - return str(self.addresses[0]) - disp = self.display_name - if disp is not None: - nameset = set(disp) - if len(nameset) > len(nameset-parser.SPECIALS): - disp = parser.quote_string(disp) - adrstr = ", ".join(str(x) for x in self.addresses) - adrstr = ' ' + adrstr if adrstr else adrstr - return "{}:{};".format(disp, adrstr) - - def __eq__(self, other): - if type(other) != type(self): - return False - return (self.display_name == other.display_name and - self.addresses == other.addresses) - - -# Header Classes # - -class BaseHeader(str): - - """Base class for message headers. - - Implements generic behavior and provides tools for subclasses. - - A subclass must define a classmethod named 'parse' that takes an unfolded - value string and a dictionary as its arguments. The dictionary will - contain one key, 'defects', initialized to an empty list. After the call - the dictionary must contain two additional keys: parse_tree, set to the - parse tree obtained from parsing the header, and 'decoded', set to the - string value of the idealized representation of the data from the value. - (That is, encoded words are decoded, and values that have canonical - representations are so represented.) - - The defects key is intended to collect parsing defects, which the message - parser will subsequently dispose of as appropriate. The parser should not, - insofar as practical, raise any errors. Defects should be added to the - list instead. The standard header parsers register defects for RFC - compliance issues, for obsolete RFC syntax, and for unrecoverable parsing - errors. - - The parse method may add additional keys to the dictionary. In this case - the subclass must define an 'init' method, which will be passed the - dictionary as its keyword arguments. The method should use (usually by - setting them as the value of similarly named attributes) and remove all the - extra keys added by its parse method, and then use super to call its parent - class with the remaining arguments and keywords. - - The subclass should also make sure that a 'max_count' attribute is defined - that is either None or 1. XXX: need to better define this API. - - """ - - def __new__(cls, name, value): - kwds = {'defects': []} - cls.parse(value, kwds) - if utils._has_surrogates(kwds['decoded']): - kwds['decoded'] = utils._sanitize(kwds['decoded']) - self = str.__new__(cls, kwds['decoded']) - # del kwds['decoded'] - self.init(name, **kwds) - return self - - def init(self, name, **_3to2kwargs): - defects = _3to2kwargs['defects']; del _3to2kwargs['defects'] - parse_tree = _3to2kwargs['parse_tree']; del _3to2kwargs['parse_tree'] - self._name = name - self._parse_tree = parse_tree - self._defects = defects - - @property - def name(self): - return self._name - - @property - def defects(self): - return tuple(self._defects) - - def __reduce__(self): - return ( - _reconstruct_header, - ( - self.__class__.__name__, - self.__class__.__bases__, - str(self), - ), - self.__dict__) - - @classmethod - def _reconstruct(cls, value): - return str.__new__(cls, value) - - def fold(self, **_3to2kwargs): - policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - """Fold header according to policy. - - The parsed representation of the header is folded according to - RFC5322 rules, as modified by the policy. If the parse tree - contains surrogateescaped bytes, the bytes are CTE encoded using - the charset 'unknown-8bit". - - Any non-ASCII characters in the parse tree are CTE encoded using - charset utf-8. XXX: make this a policy setting. - - The returned value is an ASCII-only string possibly containing linesep - characters, and ending with a linesep character. The string includes - the header name and the ': ' separator. - - """ - # At some point we need to only put fws here if it was in the source. - header = parser.Header([ - parser.HeaderLabel([ - parser.ValueTerminal(self.name, 'header-name'), - parser.ValueTerminal(':', 'header-sep')]), - parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')]), - self._parse_tree]) - return header.fold(policy=policy) - - -def _reconstruct_header(cls_name, bases, value): - return type(text_to_native_str(cls_name), bases, {})._reconstruct(value) - - -class UnstructuredHeader(object): - - max_count = None - value_parser = staticmethod(parser.get_unstructured) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = cls.value_parser(value) - kwds['decoded'] = str(kwds['parse_tree']) - - -class UniqueUnstructuredHeader(UnstructuredHeader): - - max_count = 1 - - -class DateHeader(object): - - """Header whose value consists of a single timestamp. - - Provides an additional attribute, datetime, which is either an aware - datetime using a timezone, or a naive datetime if the timezone - in the input string is -0000. Also accepts a datetime as input. - The 'value' attribute is the normalized form of the timestamp, - which means it is the output of format_datetime on the datetime. - """ - - max_count = None - - # This is used only for folding, not for creating 'decoded'. - value_parser = staticmethod(parser.get_unstructured) - - @classmethod - def parse(cls, value, kwds): - if not value: - kwds['defects'].append(errors.HeaderMissingRequiredValue()) - kwds['datetime'] = None - kwds['decoded'] = '' - kwds['parse_tree'] = parser.TokenList() - return - if isinstance(value, str): - value = utils.parsedate_to_datetime(value) - kwds['datetime'] = value - kwds['decoded'] = utils.format_datetime(kwds['datetime']) - kwds['parse_tree'] = cls.value_parser(kwds['decoded']) - - def init(self, *args, **kw): - self._datetime = kw.pop('datetime') - super().init(*args, **kw) - - @property - def datetime(self): - return self._datetime - - -class UniqueDateHeader(DateHeader): - - max_count = 1 - - -class AddressHeader(object): - - max_count = None - - @staticmethod - def value_parser(value): - address_list, value = parser.get_address_list(value) - assert not value, 'this should not happen' - return address_list - - @classmethod - def parse(cls, value, kwds): - if isinstance(value, str): - # We are translating here from the RFC language (address/mailbox) - # to our API language (group/address). - kwds['parse_tree'] = address_list = cls.value_parser(value) - groups = [] - for addr in address_list.addresses: - groups.append(Group(addr.display_name, - [Address(mb.display_name or '', - mb.local_part or '', - mb.domain or '') - for mb in addr.all_mailboxes])) - defects = list(address_list.all_defects) - else: - # Assume it is Address/Group stuff - if not hasattr(value, '__iter__'): - value = [value] - groups = [Group(None, [item]) if not hasattr(item, 'addresses') - else item - for item in value] - defects = [] - kwds['groups'] = groups - kwds['defects'] = defects - kwds['decoded'] = ', '.join([str(item) for item in groups]) - if 'parse_tree' not in kwds: - kwds['parse_tree'] = cls.value_parser(kwds['decoded']) - - def init(self, *args, **kw): - self._groups = tuple(kw.pop('groups')) - self._addresses = None - super().init(*args, **kw) - - @property - def groups(self): - return self._groups - - @property - def addresses(self): - if self._addresses is None: - self._addresses = tuple([address for group in self._groups - for address in group.addresses]) - return self._addresses - - -class UniqueAddressHeader(AddressHeader): - - max_count = 1 - - -class SingleAddressHeader(AddressHeader): - - @property - def address(self): - if len(self.addresses)!=1: - raise ValueError(("value of single address header {} is not " - "a single address").format(self.name)) - return self.addresses[0] - - -class UniqueSingleAddressHeader(SingleAddressHeader): - - max_count = 1 - - -class MIMEVersionHeader(object): - - max_count = 1 - - value_parser = staticmethod(parser.parse_mime_version) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - kwds['major'] = None if parse_tree.minor is None else parse_tree.major - kwds['minor'] = parse_tree.minor - if parse_tree.minor is not None: - kwds['version'] = '{}.{}'.format(kwds['major'], kwds['minor']) - else: - kwds['version'] = None - - def init(self, *args, **kw): - self._version = kw.pop('version') - self._major = kw.pop('major') - self._minor = kw.pop('minor') - super().init(*args, **kw) - - @property - def major(self): - return self._major - - @property - def minor(self): - return self._minor - - @property - def version(self): - return self._version - - -class ParameterizedMIMEHeader(object): - - # Mixin that handles the params dict. Must be subclassed and - # a property value_parser for the specific header provided. - - max_count = 1 - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - if parse_tree.params is None: - kwds['params'] = {} - else: - # The MIME RFCs specify that parameter ordering is arbitrary. - kwds['params'] = dict((utils._sanitize(name).lower(), - utils._sanitize(value)) - for name, value in parse_tree.params) - - def init(self, *args, **kw): - self._params = kw.pop('params') - super().init(*args, **kw) - - @property - def params(self): - return self._params.copy() - - -class ContentTypeHeader(ParameterizedMIMEHeader): - - value_parser = staticmethod(parser.parse_content_type_header) - - def init(self, *args, **kw): - super().init(*args, **kw) - self._maintype = utils._sanitize(self._parse_tree.maintype) - self._subtype = utils._sanitize(self._parse_tree.subtype) - - @property - def maintype(self): - return self._maintype - - @property - def subtype(self): - return self._subtype - - @property - def content_type(self): - return self.maintype + '/' + self.subtype - - -class ContentDispositionHeader(ParameterizedMIMEHeader): - - value_parser = staticmethod(parser.parse_content_disposition_header) - - def init(self, *args, **kw): - super().init(*args, **kw) - cd = self._parse_tree.content_disposition - self._content_disposition = cd if cd is None else utils._sanitize(cd) - - @property - def content_disposition(self): - return self._content_disposition - - -class ContentTransferEncodingHeader(object): - - max_count = 1 - - value_parser = staticmethod(parser.parse_content_transfer_encoding_header) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - - def init(self, *args, **kw): - super().init(*args, **kw) - self._cte = utils._sanitize(self._parse_tree.cte) - - @property - def cte(self): - return self._cte - - -# The header factory # - -_default_header_map = { - 'subject': UniqueUnstructuredHeader, - 'date': UniqueDateHeader, - 'resent-date': DateHeader, - 'orig-date': UniqueDateHeader, - 'sender': UniqueSingleAddressHeader, - 'resent-sender': SingleAddressHeader, - 'to': UniqueAddressHeader, - 'resent-to': AddressHeader, - 'cc': UniqueAddressHeader, - 'resent-cc': AddressHeader, - 'bcc': UniqueAddressHeader, - 'resent-bcc': AddressHeader, - 'from': UniqueAddressHeader, - 'resent-from': AddressHeader, - 'reply-to': UniqueAddressHeader, - 'mime-version': MIMEVersionHeader, - 'content-type': ContentTypeHeader, - 'content-disposition': ContentDispositionHeader, - 'content-transfer-encoding': ContentTransferEncodingHeader, - } - -class HeaderRegistry(object): - - """A header_factory and header registry.""" - - def __init__(self, base_class=BaseHeader, default_class=UnstructuredHeader, - use_default_map=True): - """Create a header_factory that works with the Policy API. - - base_class is the class that will be the last class in the created - header class's __bases__ list. default_class is the class that will be - used if "name" (see __call__) does not appear in the registry. - use_default_map controls whether or not the default mapping of names to - specialized classes is copied in to the registry when the factory is - created. The default is True. - - """ - self.registry = {} - self.base_class = base_class - self.default_class = default_class - if use_default_map: - self.registry.update(_default_header_map) - - def map_to_type(self, name, cls): - """Register cls as the specialized class for handling "name" headers. - - """ - self.registry[name.lower()] = cls - - def __getitem__(self, name): - cls = self.registry.get(name.lower(), self.default_class) - return type(text_to_native_str('_'+cls.__name__), (cls, self.base_class), {}) - - def __call__(self, name, value): - """Create a header instance for header 'name' from 'value'. - - Creates a header instance by creating a specialized class for parsing - and representing the specified header by combining the factory - base_class with a specialized class from the registry or the - default_class, and passing the name and value to the constructed - class's constructor. - - """ - return self[name](name, value) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_header_value_parser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_header_value_parser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_header_value_parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_header_value_parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2965 +0,0 @@ -"""Header value parser implementing various email-related RFC parsing rules. - -The parsing methods defined in this module implement various email related -parsing rules. Principal among them is RFC 5322, which is the followon -to RFC 2822 and primarily a clarification of the former. It also implements -RFC 2047 encoded word decoding. - -RFC 5322 goes to considerable trouble to maintain backward compatibility with -RFC 822 in the parse phase, while cleaning up the structure on the generation -phase. This parser supports correct RFC 5322 generation by tagging white space -as folding white space only when folding is allowed in the non-obsolete rule -sets. Actually, the parser is even more generous when accepting input than RFC -5322 mandates, following the spirit of Postel's Law, which RFC 5322 encourages. -Where possible deviations from the standard are annotated on the 'defects' -attribute of tokens that deviate. - -The general structure of the parser follows RFC 5322, and uses its terminology -where there is a direct correspondence. Where the implementation requires a -somewhat different structure than that used by the formal grammar, new terms -that mimic the closest existing terms are used. Thus, it really helps to have -a copy of RFC 5322 handy when studying this code. - -Input to the parser is a string that has already been unfolded according to -RFC 5322 rules. According to the RFC this unfolding is the very first step, and -this parser leaves the unfolding step to a higher level message parser, which -will have already detected the line breaks that need unfolding while -determining the beginning and end of each header. - -The output of the parser is a TokenList object, which is a list subclass. A -TokenList is a recursive data structure. The terminal nodes of the structure -are Terminal objects, which are subclasses of str. These do not correspond -directly to terminal objects in the formal grammar, but are instead more -practical higher level combinations of true terminals. - -All TokenList and Terminal objects have a 'value' attribute, which produces the -semantically meaningful value of that part of the parse subtree. The value of -all whitespace tokens (no matter how many sub-tokens they may contain) is a -single space, as per the RFC rules. This includes 'CFWS', which is herein -included in the general class of whitespace tokens. There is one exception to -the rule that whitespace tokens are collapsed into single spaces in values: in -the value of a 'bare-quoted-string' (a quoted-string with no leading or -trailing whitespace), any whitespace that appeared between the quotation marks -is preserved in the returned value. Note that in all Terminal strings quoted -pairs are turned into their unquoted values. - -All TokenList and Terminal objects also have a string value, which attempts to -be a "canonical" representation of the RFC-compliant form of the substring that -produced the parsed subtree, including minimal use of quoted pair quoting. -Whitespace runs are not collapsed. - -Comment tokens also have a 'content' attribute providing the string found -between the parens (including any nested comments) with whitespace preserved. - -All TokenList and Terminal objects have a 'defects' attribute which is a -possibly empty list all of the defects found while creating the token. Defects -may appear on any token in the tree, and a composite list of all defects in the -subtree is available through the 'all_defects' attribute of any node. (For -Terminal notes x.defects == x.all_defects.) - -Each object in a parse tree is called a 'token', and each has a 'token_type' -attribute that gives the name from the RFC 5322 grammar that it represents. -Not all RFC 5322 nodes are produced, and there is one non-RFC 5322 node that -may be produced: 'ptext'. A 'ptext' is a string of printable ascii characters. -It is returned in place of lists of (ctext/quoted-pair) and -(qtext/quoted-pair). - -XXX: provide complete list of token types. -""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import int, range, str, super, list - -import re -from collections import namedtuple, OrderedDict - -from future.backports.urllib.parse import (unquote, unquote_to_bytes) -from future.backports.email import _encoded_words as _ew -from future.backports.email import errors -from future.backports.email import utils - -# -# Useful constants and functions -# - -WSP = set(' \t') -CFWS_LEADER = WSP | set('(') -SPECIALS = set(r'()<>@,:;.\"[]') -ATOM_ENDS = SPECIALS | WSP -DOT_ATOM_ENDS = ATOM_ENDS - set('.') -# '.', '"', and '(' do not end phrases in order to support obs-phrase -PHRASE_ENDS = SPECIALS - set('."(') -TSPECIALS = (SPECIALS | set('/?=')) - set('.') -TOKEN_ENDS = TSPECIALS | WSP -ASPECIALS = TSPECIALS | set("*'%") -ATTRIBUTE_ENDS = ASPECIALS | WSP -EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%') - -def quote_string(value): - return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' - -# -# Accumulator for header folding -# - -class _Folded(object): - - def __init__(self, maxlen, policy): - self.maxlen = maxlen - self.policy = policy - self.lastlen = 0 - self.stickyspace = None - self.firstline = True - self.done = [] - self.current = list() # uses l.clear() - - def newline(self): - self.done.extend(self.current) - self.done.append(self.policy.linesep) - self.current.clear() - self.lastlen = 0 - - def finalize(self): - if self.current: - self.newline() - - def __str__(self): - return ''.join(self.done) - - def append(self, stoken): - self.current.append(stoken) - - def append_if_fits(self, token, stoken=None): - if stoken is None: - stoken = str(token) - l = len(stoken) - if self.stickyspace is not None: - stickyspace_len = len(self.stickyspace) - if self.lastlen + stickyspace_len + l <= self.maxlen: - self.current.append(self.stickyspace) - self.lastlen += stickyspace_len - self.current.append(stoken) - self.lastlen += l - self.stickyspace = None - self.firstline = False - return True - if token.has_fws: - ws = token.pop_leading_fws() - if ws is not None: - self.stickyspace += str(ws) - stickyspace_len += len(ws) - token._fold(self) - return True - if stickyspace_len and l + 1 <= self.maxlen: - margin = self.maxlen - l - if 0 < margin < stickyspace_len: - trim = stickyspace_len - margin - self.current.append(self.stickyspace[:trim]) - self.stickyspace = self.stickyspace[trim:] - stickyspace_len = trim - self.newline() - self.current.append(self.stickyspace) - self.current.append(stoken) - self.lastlen = l + stickyspace_len - self.stickyspace = None - self.firstline = False - return True - if not self.firstline: - self.newline() - self.current.append(self.stickyspace) - self.current.append(stoken) - self.stickyspace = None - self.firstline = False - return True - if self.lastlen + l <= self.maxlen: - self.current.append(stoken) - self.lastlen += l - return True - if l < self.maxlen: - self.newline() - self.current.append(stoken) - self.lastlen = l - return True - return False - -# -# TokenList and its subclasses -# - -class TokenList(list): - - token_type = None - - def __init__(self, *args, **kw): - super(TokenList, self).__init__(*args, **kw) - self.defects = [] - - def __str__(self): - return ''.join(str(x) for x in self) - - def __repr__(self): - return '{}({})'.format(self.__class__.__name__, - super(TokenList, self).__repr__()) - - @property - def value(self): - return ''.join(x.value for x in self if x.value) - - @property - def all_defects(self): - return sum((x.all_defects for x in self), self.defects) - - # - # Folding API - # - # parts(): - # - # return a list of objects that constitute the "higher level syntactic - # objects" specified by the RFC as the best places to fold a header line. - # The returned objects must include leading folding white space, even if - # this means mutating the underlying parse tree of the object. Each object - # is only responsible for returning *its* parts, and should not drill down - # to any lower level except as required to meet the leading folding white - # space constraint. - # - # _fold(folded): - # - # folded: the result accumulator. This is an instance of _Folded. - # (XXX: I haven't finished factoring this out yet, the folding code - # pretty much uses this as a state object.) When the folded.current - # contains as much text as will fit, the _fold method should call - # folded.newline. - # folded.lastlen: the current length of the test stored in folded.current. - # folded.maxlen: The maximum number of characters that may appear on a - # folded line. Differs from the policy setting in that "no limit" is - # represented by +inf, which means it can be used in the trivially - # logical fashion in comparisons. - # - # Currently no subclasses implement parts, and I think this will remain - # true. A subclass only needs to implement _fold when the generic version - # isn't sufficient. _fold will need to be implemented primarily when it is - # possible for encoded words to appear in the specialized token-list, since - # there is no generic algorithm that can know where exactly the encoded - # words are allowed. A _fold implementation is responsible for filling - # lines in the same general way that the top level _fold does. It may, and - # should, call the _fold method of sub-objects in a similar fashion to that - # of the top level _fold. - # - # XXX: I'm hoping it will be possible to factor the existing code further - # to reduce redundancy and make the logic clearer. - - @property - def parts(self): - klass = self.__class__ - this = list() - for token in self: - if token.startswith_fws(): - if this: - yield this[0] if len(this)==1 else klass(this) - this.clear() - end_ws = token.pop_trailing_ws() - this.append(token) - if end_ws: - yield klass(this) - this = [end_ws] - if this: - yield this[0] if len(this)==1 else klass(this) - - def startswith_fws(self): - return self[0].startswith_fws() - - def pop_leading_fws(self): - if self[0].token_type == 'fws': - return self.pop(0) - return self[0].pop_leading_fws() - - def pop_trailing_ws(self): - if self[-1].token_type == 'cfws': - return self.pop(-1) - return self[-1].pop_trailing_ws() - - @property - def has_fws(self): - for part in self: - if part.has_fws: - return True - return False - - def has_leading_comment(self): - return self[0].has_leading_comment() - - @property - def comments(self): - comments = [] - for token in self: - comments.extend(token.comments) - return comments - - def fold(self, **_3to2kwargs): - # max_line_length 0/None means no limit, ie: infinitely long. - policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - maxlen = policy.max_line_length or float("+inf") - folded = _Folded(maxlen, policy) - self._fold(folded) - folded.finalize() - return str(folded) - - def as_encoded_word(self, charset): - # This works only for things returned by 'parts', which include - # the leading fws, if any, that should be used. - res = [] - ws = self.pop_leading_fws() - if ws: - res.append(ws) - trailer = self.pop(-1) if self[-1].token_type=='fws' else '' - res.append(_ew.encode(str(self), charset)) - res.append(trailer) - return ''.join(res) - - def cte_encode(self, charset, policy): - res = [] - for part in self: - res.append(part.cte_encode(charset, policy)) - return ''.join(res) - - def _fold(self, folded): - for part in self.parts: - tstr = str(part) - tlen = len(tstr) - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - # XXX: this should be a policy setting - charset = 'utf-8' - tstr = part.cte_encode(charset, folded.policy) - tlen = len(tstr) - if folded.append_if_fits(part, tstr): - continue - # Peel off the leading whitespace if any and make it sticky, to - # avoid infinite recursion. - ws = part.pop_leading_fws() - if ws is not None: - # Peel off the leading whitespace and make it sticky, to - # avoid infinite recursion. - folded.stickyspace = str(part.pop(0)) - if folded.append_if_fits(part): - continue - if part.has_fws: - part._fold(folded) - continue - # There are no fold points in this one; it is too long for a single - # line and can't be split...we just have to put it on its own line. - folded.append(tstr) - folded.newline() - - def pprint(self, indent=''): - print('\n'.join(self._pp(indent=''))) - - def ppstr(self, indent=''): - return '\n'.join(self._pp(indent='')) - - def _pp(self, indent=''): - yield '{}{}/{}('.format( - indent, - self.__class__.__name__, - self.token_type) - for token in self: - if not hasattr(token, '_pp'): - yield (indent + ' !! invalid element in token ' - 'list: {!r}'.format(token)) - else: - for line in token._pp(indent+' '): - yield line - if self.defects: - extra = ' Defects: {}'.format(self.defects) - else: - extra = '' - yield '{}){}'.format(indent, extra) - - -class WhiteSpaceTokenList(TokenList): - - @property - def value(self): - return ' ' - - @property - def comments(self): - return [x.content for x in self if x.token_type=='comment'] - - -class UnstructuredTokenList(TokenList): - - token_type = 'unstructured' - - def _fold(self, folded): - if any(x.token_type=='encoded-word' for x in self): - return self._fold_encoded(folded) - # Here we can have either a pure ASCII string that may or may not - # have surrogateescape encoded bytes, or a unicode string. - last_ew = None - for part in self.parts: - tstr = str(part) - is_ew = False - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - charset = 'utf-8' - if last_ew is not None: - # We've already done an EW, combine this one with it - # if there's room. - chunk = get_unstructured( - ''.join(folded.current[last_ew:]+[tstr])).as_encoded_word(charset) - oldlastlen = sum(len(x) for x in folded.current[:last_ew]) - schunk = str(chunk) - lchunk = len(schunk) - if oldlastlen + lchunk <= folded.maxlen: - del folded.current[last_ew:] - folded.append(schunk) - folded.lastlen = oldlastlen + lchunk - continue - tstr = part.as_encoded_word(charset) - is_ew = True - if folded.append_if_fits(part, tstr): - if is_ew: - last_ew = len(folded.current) - 1 - continue - if is_ew or last_ew: - # It's too big to fit on the line, but since we've - # got encoded words we can use encoded word folding. - part._fold_as_ew(folded) - continue - # Peel off the leading whitespace if any and make it sticky, to - # avoid infinite recursion. - ws = part.pop_leading_fws() - if ws is not None: - folded.stickyspace = str(ws) - if folded.append_if_fits(part): - continue - if part.has_fws: - part.fold(folded) - continue - # It can't be split...we just have to put it on its own line. - folded.append(tstr) - folded.newline() - last_ew = None - - def cte_encode(self, charset, policy): - res = [] - last_ew = None - for part in self: - spart = str(part) - try: - spart.encode('us-ascii') - res.append(spart) - except UnicodeEncodeError: - if last_ew is None: - res.append(part.cte_encode(charset, policy)) - last_ew = len(res) - else: - tl = get_unstructured(''.join(res[last_ew:] + [spart])) - res.append(tl.as_encoded_word()) - return ''.join(res) - - -class Phrase(TokenList): - - token_type = 'phrase' - - def _fold(self, folded): - # As with Unstructured, we can have pure ASCII with or without - # surrogateescape encoded bytes, or we could have unicode. But this - # case is more complicated, since we have to deal with the various - # sub-token types and how they can be composed in the face of - # unicode-that-needs-CTE-encoding, and the fact that if a token a - # comment that becomes a barrier across which we can't compose encoded - # words. - last_ew = None - for part in self.parts: - tstr = str(part) - tlen = len(tstr) - has_ew = False - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - charset = 'utf-8' - if last_ew is not None and not part.has_leading_comment(): - # We've already done an EW, let's see if we can combine - # this one with it. The last_ew logic ensures that all we - # have at this point is atoms, no comments or quoted - # strings. So we can treat the text between the last - # encoded word and the content of this token as - # unstructured text, and things will work correctly. But - # we have to strip off any trailing comment on this token - # first, and if it is a quoted string we have to pull out - # the content (we're encoding it, so it no longer needs to - # be quoted). - if part[-1].token_type == 'cfws' and part.comments: - remainder = part.pop(-1) - else: - remainder = '' - for i, token in enumerate(part): - if token.token_type == 'bare-quoted-string': - part[i] = UnstructuredTokenList(token[:]) - chunk = get_unstructured( - ''.join(folded.current[last_ew:]+[tstr])).as_encoded_word(charset) - schunk = str(chunk) - lchunk = len(schunk) - if last_ew + lchunk <= folded.maxlen: - del folded.current[last_ew:] - folded.append(schunk) - folded.lastlen = sum(len(x) for x in folded.current) - continue - tstr = part.as_encoded_word(charset) - tlen = len(tstr) - has_ew = True - if folded.append_if_fits(part, tstr): - if has_ew and not part.comments: - last_ew = len(folded.current) - 1 - elif part.comments or part.token_type == 'quoted-string': - # If a comment is involved we can't combine EWs. And if a - # quoted string is involved, it's not worth the effort to - # try to combine them. - last_ew = None - continue - part._fold(folded) - - def cte_encode(self, charset, policy): - res = [] - last_ew = None - is_ew = False - for part in self: - spart = str(part) - try: - spart.encode('us-ascii') - res.append(spart) - except UnicodeEncodeError: - is_ew = True - if last_ew is None: - if not part.comments: - last_ew = len(res) - res.append(part.cte_encode(charset, policy)) - elif not part.has_leading_comment(): - if part[-1].token_type == 'cfws' and part.comments: - remainder = part.pop(-1) - else: - remainder = '' - for i, token in enumerate(part): - if token.token_type == 'bare-quoted-string': - part[i] = UnstructuredTokenList(token[:]) - tl = get_unstructured(''.join(res[last_ew:] + [spart])) - res[last_ew:] = [tl.as_encoded_word(charset)] - if part.comments or (not is_ew and part.token_type == 'quoted-string'): - last_ew = None - return ''.join(res) - -class Word(TokenList): - - token_type = 'word' - - -class CFWSList(WhiteSpaceTokenList): - - token_type = 'cfws' - - def has_leading_comment(self): - return bool(self.comments) - - -class Atom(TokenList): - - token_type = 'atom' - - -class Token(TokenList): - - token_type = 'token' - - -class EncodedWord(TokenList): - - token_type = 'encoded-word' - cte = None - charset = None - lang = None - - @property - def encoded(self): - if self.cte is not None: - return self.cte - _ew.encode(str(self), self.charset) - - - -class QuotedString(TokenList): - - token_type = 'quoted-string' - - @property - def content(self): - for x in self: - if x.token_type == 'bare-quoted-string': - return x.value - - @property - def quoted_value(self): - res = [] - for x in self: - if x.token_type == 'bare-quoted-string': - res.append(str(x)) - else: - res.append(x.value) - return ''.join(res) - - @property - def stripped_value(self): - for token in self: - if token.token_type == 'bare-quoted-string': - return token.value - - -class BareQuotedString(QuotedString): - - token_type = 'bare-quoted-string' - - def __str__(self): - return quote_string(''.join(str(x) for x in self)) - - @property - def value(self): - return ''.join(str(x) for x in self) - - -class Comment(WhiteSpaceTokenList): - - token_type = 'comment' - - def __str__(self): - return ''.join(sum([ - ["("], - [self.quote(x) for x in self], - [")"], - ], [])) - - def quote(self, value): - if value.token_type == 'comment': - return str(value) - return str(value).replace('\\', '\\\\').replace( - '(', '\(').replace( - ')', '\)') - - @property - def content(self): - return ''.join(str(x) for x in self) - - @property - def comments(self): - return [self.content] - -class AddressList(TokenList): - - token_type = 'address-list' - - @property - def addresses(self): - return [x for x in self if x.token_type=='address'] - - @property - def mailboxes(self): - return sum((x.mailboxes - for x in self if x.token_type=='address'), []) - - @property - def all_mailboxes(self): - return sum((x.all_mailboxes - for x in self if x.token_type=='address'), []) - - -class Address(TokenList): - - token_type = 'address' - - @property - def display_name(self): - if self[0].token_type == 'group': - return self[0].display_name - - @property - def mailboxes(self): - if self[0].token_type == 'mailbox': - return [self[0]] - elif self[0].token_type == 'invalid-mailbox': - return [] - return self[0].mailboxes - - @property - def all_mailboxes(self): - if self[0].token_type == 'mailbox': - return [self[0]] - elif self[0].token_type == 'invalid-mailbox': - return [self[0]] - return self[0].all_mailboxes - -class MailboxList(TokenList): - - token_type = 'mailbox-list' - - @property - def mailboxes(self): - return [x for x in self if x.token_type=='mailbox'] - - @property - def all_mailboxes(self): - return [x for x in self - if x.token_type in ('mailbox', 'invalid-mailbox')] - - -class GroupList(TokenList): - - token_type = 'group-list' - - @property - def mailboxes(self): - if not self or self[0].token_type != 'mailbox-list': - return [] - return self[0].mailboxes - - @property - def all_mailboxes(self): - if not self or self[0].token_type != 'mailbox-list': - return [] - return self[0].all_mailboxes - - -class Group(TokenList): - - token_type = "group" - - @property - def mailboxes(self): - if self[2].token_type != 'group-list': - return [] - return self[2].mailboxes - - @property - def all_mailboxes(self): - if self[2].token_type != 'group-list': - return [] - return self[2].all_mailboxes - - @property - def display_name(self): - return self[0].display_name - - -class NameAddr(TokenList): - - token_type = 'name-addr' - - @property - def display_name(self): - if len(self) == 1: - return None - return self[0].display_name - - @property - def local_part(self): - return self[-1].local_part - - @property - def domain(self): - return self[-1].domain - - @property - def route(self): - return self[-1].route - - @property - def addr_spec(self): - return self[-1].addr_spec - - -class AngleAddr(TokenList): - - token_type = 'angle-addr' - - @property - def local_part(self): - for x in self: - if x.token_type == 'addr-spec': - return x.local_part - - @property - def domain(self): - for x in self: - if x.token_type == 'addr-spec': - return x.domain - - @property - def route(self): - for x in self: - if x.token_type == 'obs-route': - return x.domains - - @property - def addr_spec(self): - for x in self: - if x.token_type == 'addr-spec': - return x.addr_spec - else: - return '<>' - - -class ObsRoute(TokenList): - - token_type = 'obs-route' - - @property - def domains(self): - return [x.domain for x in self if x.token_type == 'domain'] - - -class Mailbox(TokenList): - - token_type = 'mailbox' - - @property - def display_name(self): - if self[0].token_type == 'name-addr': - return self[0].display_name - - @property - def local_part(self): - return self[0].local_part - - @property - def domain(self): - return self[0].domain - - @property - def route(self): - if self[0].token_type == 'name-addr': - return self[0].route - - @property - def addr_spec(self): - return self[0].addr_spec - - -class InvalidMailbox(TokenList): - - token_type = 'invalid-mailbox' - - @property - def display_name(self): - return None - - local_part = domain = route = addr_spec = display_name - - -class Domain(TokenList): - - token_type = 'domain' - - @property - def domain(self): - return ''.join(super(Domain, self).value.split()) - - -class DotAtom(TokenList): - - token_type = 'dot-atom' - - -class DotAtomText(TokenList): - - token_type = 'dot-atom-text' - - -class AddrSpec(TokenList): - - token_type = 'addr-spec' - - @property - def local_part(self): - return self[0].local_part - - @property - def domain(self): - if len(self) < 3: - return None - return self[-1].domain - - @property - def value(self): - if len(self) < 3: - return self[0].value - return self[0].value.rstrip()+self[1].value+self[2].value.lstrip() - - @property - def addr_spec(self): - nameset = set(self.local_part) - if len(nameset) > len(nameset-DOT_ATOM_ENDS): - lp = quote_string(self.local_part) - else: - lp = self.local_part - if self.domain is not None: - return lp + '@' + self.domain - return lp - - -class ObsLocalPart(TokenList): - - token_type = 'obs-local-part' - - -class DisplayName(Phrase): - - token_type = 'display-name' - - @property - def display_name(self): - res = TokenList(self) - if res[0].token_type == 'cfws': - res.pop(0) - else: - if res[0][0].token_type == 'cfws': - res[0] = TokenList(res[0][1:]) - if res[-1].token_type == 'cfws': - res.pop() - else: - if res[-1][-1].token_type == 'cfws': - res[-1] = TokenList(res[-1][:-1]) - return res.value - - @property - def value(self): - quote = False - if self.defects: - quote = True - else: - for x in self: - if x.token_type == 'quoted-string': - quote = True - if quote: - pre = post = '' - if self[0].token_type=='cfws' or self[0][0].token_type=='cfws': - pre = ' ' - if self[-1].token_type=='cfws' or self[-1][-1].token_type=='cfws': - post = ' ' - return pre+quote_string(self.display_name)+post - else: - return super(DisplayName, self).value - - -class LocalPart(TokenList): - - token_type = 'local-part' - - @property - def value(self): - if self[0].token_type == "quoted-string": - return self[0].quoted_value - else: - return self[0].value - - @property - def local_part(self): - # Strip whitespace from front, back, and around dots. - res = [DOT] - last = DOT - last_is_tl = False - for tok in self[0] + [DOT]: - if tok.token_type == 'cfws': - continue - if (last_is_tl and tok.token_type == 'dot' and - last[-1].token_type == 'cfws'): - res[-1] = TokenList(last[:-1]) - is_tl = isinstance(tok, TokenList) - if (is_tl and last.token_type == 'dot' and - tok[0].token_type == 'cfws'): - res.append(TokenList(tok[1:])) - else: - res.append(tok) - last = res[-1] - last_is_tl = is_tl - res = TokenList(res[1:-1]) - return res.value - - -class DomainLiteral(TokenList): - - token_type = 'domain-literal' - - @property - def domain(self): - return ''.join(super(DomainLiteral, self).value.split()) - - @property - def ip(self): - for x in self: - if x.token_type == 'ptext': - return x.value - - -class MIMEVersion(TokenList): - - token_type = 'mime-version' - major = None - minor = None - - -class Parameter(TokenList): - - token_type = 'parameter' - sectioned = False - extended = False - charset = 'us-ascii' - - @property - def section_number(self): - # Because the first token, the attribute (name) eats CFWS, the second - # token is always the section if there is one. - return self[1].number if self.sectioned else 0 - - @property - def param_value(self): - # This is part of the "handle quoted extended parameters" hack. - for token in self: - if token.token_type == 'value': - return token.stripped_value - if token.token_type == 'quoted-string': - for token in token: - if token.token_type == 'bare-quoted-string': - for token in token: - if token.token_type == 'value': - return token.stripped_value - return '' - - -class InvalidParameter(Parameter): - - token_type = 'invalid-parameter' - - -class Attribute(TokenList): - - token_type = 'attribute' - - @property - def stripped_value(self): - for token in self: - if token.token_type.endswith('attrtext'): - return token.value - -class Section(TokenList): - - token_type = 'section' - number = None - - -class Value(TokenList): - - token_type = 'value' - - @property - def stripped_value(self): - token = self[0] - if token.token_type == 'cfws': - token = self[1] - if token.token_type.endswith( - ('quoted-string', 'attribute', 'extended-attribute')): - return token.stripped_value - return self.value - - -class MimeParameters(TokenList): - - token_type = 'mime-parameters' - - @property - def params(self): - # The RFC specifically states that the ordering of parameters is not - # guaranteed and may be reordered by the transport layer. So we have - # to assume the RFC 2231 pieces can come in any order. However, we - # output them in the order that we first see a given name, which gives - # us a stable __str__. - params = OrderedDict() - for token in self: - if not token.token_type.endswith('parameter'): - continue - if token[0].token_type != 'attribute': - continue - name = token[0].value.strip() - if name not in params: - params[name] = [] - params[name].append((token.section_number, token)) - for name, parts in params.items(): - parts = sorted(parts) - # XXX: there might be more recovery we could do here if, for - # example, this is really a case of a duplicate attribute name. - value_parts = [] - charset = parts[0][1].charset - for i, (section_number, param) in enumerate(parts): - if section_number != i: - param.defects.append(errors.InvalidHeaderDefect( - "inconsistent multipart parameter numbering")) - value = param.param_value - if param.extended: - try: - value = unquote_to_bytes(value) - except UnicodeEncodeError: - # source had surrogate escaped bytes. What we do now - # is a bit of an open question. I'm not sure this is - # the best choice, but it is what the old algorithm did - value = unquote(value, encoding='latin-1') - else: - try: - value = value.decode(charset, 'surrogateescape') - except LookupError: - # XXX: there should really be a custom defect for - # unknown character set to make it easy to find, - # because otherwise unknown charset is a silent - # failure. - value = value.decode('us-ascii', 'surrogateescape') - if utils._has_surrogates(value): - param.defects.append(errors.UndecodableBytesDefect()) - value_parts.append(value) - value = ''.join(value_parts) - yield name, value - - def __str__(self): - params = [] - for name, value in self.params: - if value: - params.append('{}={}'.format(name, quote_string(value))) - else: - params.append(name) - params = '; '.join(params) - return ' ' + params if params else '' - - -class ParameterizedHeaderValue(TokenList): - - @property - def params(self): - for token in reversed(self): - if token.token_type == 'mime-parameters': - return token.params - return {} - - @property - def parts(self): - if self and self[-1].token_type == 'mime-parameters': - # We don't want to start a new line if all of the params don't fit - # after the value, so unwrap the parameter list. - return TokenList(self[:-1] + self[-1]) - return TokenList(self).parts - - -class ContentType(ParameterizedHeaderValue): - - token_type = 'content-type' - maintype = 'text' - subtype = 'plain' - - -class ContentDisposition(ParameterizedHeaderValue): - - token_type = 'content-disposition' - content_disposition = None - - -class ContentTransferEncoding(TokenList): - - token_type = 'content-transfer-encoding' - cte = '7bit' - - -class HeaderLabel(TokenList): - - token_type = 'header-label' - - -class Header(TokenList): - - token_type = 'header' - - def _fold(self, folded): - folded.append(str(self.pop(0))) - folded.lastlen = len(folded.current[0]) - # The first line of the header is different from all others: we don't - # want to start a new object on a new line if it has any fold points in - # it that would allow part of it to be on the first header line. - # Further, if the first fold point would fit on the new line, we want - # to do that, but if it doesn't we want to put it on the first line. - # Folded supports this via the stickyspace attribute. If this - # attribute is not None, it does the special handling. - folded.stickyspace = str(self.pop(0)) if self[0].token_type == 'cfws' else '' - rest = self.pop(0) - if self: - raise ValueError("Malformed Header token list") - rest._fold(folded) - - -# -# Terminal classes and instances -# - -class Terminal(str): - - def __new__(cls, value, token_type): - self = super(Terminal, cls).__new__(cls, value) - self.token_type = token_type - self.defects = [] - return self - - def __repr__(self): - return "{}({})".format(self.__class__.__name__, super(Terminal, self).__repr__()) - - @property - def all_defects(self): - return list(self.defects) - - def _pp(self, indent=''): - return ["{}{}/{}({}){}".format( - indent, - self.__class__.__name__, - self.token_type, - super(Terminal, self).__repr__(), - '' if not self.defects else ' {}'.format(self.defects), - )] - - def cte_encode(self, charset, policy): - value = str(self) - try: - value.encode('us-ascii') - return value - except UnicodeEncodeError: - return _ew.encode(value, charset) - - def pop_trailing_ws(self): - # This terminates the recursion. - return None - - def pop_leading_fws(self): - # This terminates the recursion. - return None - - @property - def comments(self): - return [] - - def has_leading_comment(self): - return False - - def __getnewargs__(self): - return(str(self), self.token_type) - - -class WhiteSpaceTerminal(Terminal): - - @property - def value(self): - return ' ' - - def startswith_fws(self): - return True - - has_fws = True - - -class ValueTerminal(Terminal): - - @property - def value(self): - return self - - def startswith_fws(self): - return False - - has_fws = False - - def as_encoded_word(self, charset): - return _ew.encode(str(self), charset) - - -class EWWhiteSpaceTerminal(WhiteSpaceTerminal): - - @property - def value(self): - return '' - - @property - def encoded(self): - return self[:] - - def __str__(self): - return '' - - has_fws = True - - -# XXX these need to become classes and used as instances so -# that a program can't change them in a parse tree and screw -# up other parse trees. Maybe should have tests for that, too. -DOT = ValueTerminal('.', 'dot') -ListSeparator = ValueTerminal(',', 'list-separator') -RouteComponentMarker = ValueTerminal('@', 'route-component-marker') - -# -# Parser -# - -"""Parse strings according to RFC822/2047/2822/5322 rules. - -This is a stateless parser. Each get_XXX function accepts a string and -returns either a Terminal or a TokenList representing the RFC object named -by the method and a string containing the remaining unparsed characters -from the input. Thus a parser method consumes the next syntactic construct -of a given type and returns a token representing the construct plus the -unparsed remainder of the input string. - -For example, if the first element of a structured header is a 'phrase', -then: - - phrase, value = get_phrase(value) - -returns the complete phrase from the start of the string value, plus any -characters left in the string after the phrase is removed. - -""" - -_wsp_splitter = re.compile(r'([{}]+)'.format(''.join(WSP))).split -_non_atom_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATOM_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_printable_finder = re.compile(r"[\x00-\x20\x7F]").findall -_non_token_end_matcher = re.compile(r"[^{}]+".format( - ''.join(TOKEN_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_attribute_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATTRIBUTE_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_extended_attribute_end_matcher = re.compile(r"[^{}]+".format( - ''.join(EXTENDED_ATTRIBUTE_ENDS).replace( - '\\','\\\\').replace(']','\]'))).match - -def _validate_xtext(xtext): - """If input token contains ASCII non-printables, register a defect.""" - - non_printables = _non_printable_finder(xtext) - if non_printables: - xtext.defects.append(errors.NonPrintableDefect(non_printables)) - if utils._has_surrogates(xtext): - xtext.defects.append(errors.UndecodableBytesDefect( - "Non-ASCII characters found in header token")) - -def _get_ptext_to_endchars(value, endchars): - """Scan printables/quoted-pairs until endchars and return unquoted ptext. - - This function turns a run of qcontent, ccontent-without-comments, or - dtext-with-quoted-printables into a single string by unquoting any - quoted printables. It returns the string, the remaining value, and - a flag that is True iff there were any quoted printables decoded. - - """ - _3to2list = list(_wsp_splitter(value, 1)) - fragment, remainder, = _3to2list[:1] + [_3to2list[1:]] - vchars = [] - escape = False - had_qp = False - for pos in range(len(fragment)): - if fragment[pos] == '\\': - if escape: - escape = False - had_qp = True - else: - escape = True - continue - if escape: - escape = False - elif fragment[pos] in endchars: - break - vchars.append(fragment[pos]) - else: - pos = pos + 1 - return ''.join(vchars), ''.join([fragment[pos:]] + remainder), had_qp - -def _decode_ew_run(value): - """ Decode a run of RFC2047 encoded words. - - _decode_ew_run(value) -> (text, value, defects) - - Scans the supplied value for a run of tokens that look like they are RFC - 2047 encoded words, decodes those words into text according to RFC 2047 - rules (whitespace between encoded words is discarded), and returns the text - and the remaining value (including any leading whitespace on the remaining - value), as well as a list of any defects encountered while decoding. The - input value may not have any leading whitespace. - - """ - res = [] - defects = [] - last_ws = '' - while value: - try: - tok, ws, value = _wsp_splitter(value, 1) - except ValueError: - tok, ws, value = value, '', '' - if not (tok.startswith('=?') and tok.endswith('?=')): - return ''.join(res), last_ws + tok + ws + value, defects - text, charset, lang, new_defects = _ew.decode(tok) - res.append(text) - defects.extend(new_defects) - last_ws = ws - return ''.join(res), last_ws, defects - -def get_fws(value): - """FWS = 1*WSP - - This isn't the RFC definition. We're using fws to represent tokens where - folding can be done, but when we are parsing the *un*folding has already - been done so we don't need to watch out for CRLF. - - """ - newvalue = value.lstrip() - fws = WhiteSpaceTerminal(value[:len(value)-len(newvalue)], 'fws') - return fws, newvalue - -def get_encoded_word(value): - """ encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - - """ - ew = EncodedWord() - if not value.startswith('=?'): - raise errors.HeaderParseError( - "expected encoded word but found {}".format(value)) - _3to2list1 = list(value[2:].split('?=', 1)) - tok, remainder, = _3to2list1[:1] + [_3to2list1[1:]] - if tok == value[2:]: - raise errors.HeaderParseError( - "expected encoded word but found {}".format(value)) - remstr = ''.join(remainder) - if remstr[:2].isdigit(): - _3to2list3 = list(remstr.split('?=', 1)) - rest, remainder, = _3to2list3[:1] + [_3to2list3[1:]] - tok = tok + '?=' + rest - if len(tok.split()) > 1: - ew.defects.append(errors.InvalidHeaderDefect( - "whitespace inside encoded word")) - ew.cte = value - value = ''.join(remainder) - try: - text, charset, lang, defects = _ew.decode('=?' + tok + '?=') - except ValueError: - raise errors.HeaderParseError( - "encoded word format invalid: '{}'".format(ew.cte)) - ew.charset = charset - ew.lang = lang - ew.defects.extend(defects) - while text: - if text[0] in WSP: - token, text = get_fws(text) - ew.append(token) - continue - _3to2list5 = list(_wsp_splitter(text, 1)) - chars, remainder, = _3to2list5[:1] + [_3to2list5[1:]] - vtext = ValueTerminal(chars, 'vtext') - _validate_xtext(vtext) - ew.append(vtext) - text = ''.join(remainder) - return ew, value - -def get_unstructured(value): - """unstructured = (*([FWS] vchar) *WSP) / obs-unstruct - obs-unstruct = *((*LF *CR *(obs-utext) *LF *CR)) / FWS) - obs-utext = %d0 / obs-NO-WS-CTL / LF / CR - - obs-NO-WS-CTL is control characters except WSP/CR/LF. - - So, basically, we have printable runs, plus control characters or nulls in - the obsolete syntax, separated by whitespace. Since RFC 2047 uses the - obsolete syntax in its specification, but requires whitespace on either - side of the encoded words, I can see no reason to need to separate the - non-printable-non-whitespace from the printable runs if they occur, so we - parse this into xtext tokens separated by WSP tokens. - - Because an 'unstructured' value must by definition constitute the entire - value, this 'get' routine does not return a remaining value, only the - parsed TokenList. - - """ - # XXX: but what about bare CR and LF? They might signal the start or - # end of an encoded word. YAGNI for now, since out current parsers - # will never send us strings with bard CR or LF. - - unstructured = UnstructuredTokenList() - while value: - if value[0] in WSP: - token, value = get_fws(value) - unstructured.append(token) - continue - if value.startswith('=?'): - try: - token, value = get_encoded_word(value) - except errors.HeaderParseError: - pass - else: - have_ws = True - if len(unstructured) > 0: - if unstructured[-1].token_type != 'fws': - unstructured.defects.append(errors.InvalidHeaderDefect( - "missing whitespace before encoded word")) - have_ws = False - if have_ws and len(unstructured) > 1: - if unstructured[-2].token_type == 'encoded-word': - unstructured[-1] = EWWhiteSpaceTerminal( - unstructured[-1], 'fws') - unstructured.append(token) - continue - _3to2list7 = list(_wsp_splitter(value, 1)) - tok, remainder, = _3to2list7[:1] + [_3to2list7[1:]] - vtext = ValueTerminal(tok, 'vtext') - _validate_xtext(vtext) - unstructured.append(vtext) - value = ''.join(remainder) - return unstructured - -def get_qp_ctext(value): - """ctext = - - This is not the RFC ctext, since we are handling nested comments in comment - and unquoting quoted-pairs here. We allow anything except the '()' - characters, but if we find any ASCII other than the RFC defined printable - ASCII an NonPrintableDefect is added to the token's defects list. Since - quoted pairs are converted to their unquoted values, what is returned is - a 'ptext' token. In this case it is a WhiteSpaceTerminal, so it's value - is ' '. - - """ - ptext, value, _ = _get_ptext_to_endchars(value, '()') - ptext = WhiteSpaceTerminal(ptext, 'ptext') - _validate_xtext(ptext) - return ptext, value - -def get_qcontent(value): - """qcontent = qtext / quoted-pair - - We allow anything except the DQUOTE character, but if we find any ASCII - other than the RFC defined printable ASCII an NonPrintableDefect is - added to the token's defects list. Any quoted pairs are converted to their - unquoted values, so what is returned is a 'ptext' token. In this case it - is a ValueTerminal. - - """ - ptext, value, _ = _get_ptext_to_endchars(value, '"') - ptext = ValueTerminal(ptext, 'ptext') - _validate_xtext(ptext) - return ptext, value - -def get_atext(value): - """atext = - - We allow any non-ATOM_ENDS in atext, but add an InvalidATextDefect to - the token's defects list if we find non-atext characters. - """ - m = _non_atom_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected atext but found '{}'".format(value)) - atext = m.group() - value = value[len(atext):] - atext = ValueTerminal(atext, 'atext') - _validate_xtext(atext) - return atext, value - -def get_bare_quoted_string(value): - """bare-quoted-string = DQUOTE *([FWS] qcontent) [FWS] DQUOTE - - A quoted-string without the leading or trailing white space. Its - value is the text between the quote marks, with whitespace - preserved and quoted pairs decoded. - """ - if value[0] != '"': - raise errors.HeaderParseError( - "expected '\"' but found '{}'".format(value)) - bare_quoted_string = BareQuotedString() - value = value[1:] - while value and value[0] != '"': - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_qcontent(value) - bare_quoted_string.append(token) - if not value: - bare_quoted_string.defects.append(errors.InvalidHeaderDefect( - "end of header inside quoted string")) - return bare_quoted_string, value - return bare_quoted_string, value[1:] - -def get_comment(value): - """comment = "(" *([FWS] ccontent) [FWS] ")" - ccontent = ctext / quoted-pair / comment - - We handle nested comments here, and quoted-pair in our qp-ctext routine. - """ - if value and value[0] != '(': - raise errors.HeaderParseError( - "expected '(' but found '{}'".format(value)) - comment = Comment() - value = value[1:] - while value and value[0] != ")": - if value[0] in WSP: - token, value = get_fws(value) - elif value[0] == '(': - token, value = get_comment(value) - else: - token, value = get_qp_ctext(value) - comment.append(token) - if not value: - comment.defects.append(errors.InvalidHeaderDefect( - "end of header inside comment")) - return comment, value - return comment, value[1:] - -def get_cfws(value): - """CFWS = (1*([FWS] comment) [FWS]) / FWS - - """ - cfws = CFWSList() - while value and value[0] in CFWS_LEADER: - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_comment(value) - cfws.append(token) - return cfws, value - -def get_quoted_string(value): - """quoted-string = [CFWS] [CFWS] - - 'bare-quoted-string' is an intermediate class defined by this - parser and not by the RFC grammar. It is the quoted string - without any attached CFWS. - """ - quoted_string = QuotedString() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - quoted_string.append(token) - token, value = get_bare_quoted_string(value) - quoted_string.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - quoted_string.append(token) - return quoted_string, value - -def get_atom(value): - """atom = [CFWS] 1*atext [CFWS] - - """ - atom = Atom() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - atom.append(token) - if value and value[0] in ATOM_ENDS: - raise errors.HeaderParseError( - "expected atom but found '{}'".format(value)) - token, value = get_atext(value) - atom.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - atom.append(token) - return atom, value - -def get_dot_atom_text(value): - """ dot-text = 1*atext *("." 1*atext) - - """ - dot_atom_text = DotAtomText() - if not value or value[0] in ATOM_ENDS: - raise errors.HeaderParseError("expected atom at a start of " - "dot-atom-text but found '{}'".format(value)) - while value and value[0] not in ATOM_ENDS: - token, value = get_atext(value) - dot_atom_text.append(token) - if value and value[0] == '.': - dot_atom_text.append(DOT) - value = value[1:] - if dot_atom_text[-1] is DOT: - raise errors.HeaderParseError("expected atom at end of dot-atom-text " - "but found '{}'".format('.'+value)) - return dot_atom_text, value - -def get_dot_atom(value): - """ dot-atom = [CFWS] dot-atom-text [CFWS] - - """ - dot_atom = DotAtom() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - dot_atom.append(token) - token, value = get_dot_atom_text(value) - dot_atom.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - dot_atom.append(token) - return dot_atom, value - -def get_word(value): - """word = atom / quoted-string - - Either atom or quoted-string may start with CFWS. We have to peel off this - CFWS first to determine which type of word to parse. Afterward we splice - the leading CFWS, if any, into the parsed sub-token. - - If neither an atom or a quoted-string is found before the next special, a - HeaderParseError is raised. - - The token returned is either an Atom or a QuotedString, as appropriate. - This means the 'word' level of the formal grammar is not represented in the - parse tree; this is because having that extra layer when manipulating the - parse tree is more confusing than it is helpful. - - """ - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - else: - leader = None - if value[0]=='"': - token, value = get_quoted_string(value) - elif value[0] in SPECIALS: - raise errors.HeaderParseError("Expected 'atom' or 'quoted-string' " - "but found '{}'".format(value)) - else: - token, value = get_atom(value) - if leader is not None: - token[:0] = [leader] - return token, value - -def get_phrase(value): - """ phrase = 1*word / obs-phrase - obs-phrase = word *(word / "." / CFWS) - - This means a phrase can be a sequence of words, periods, and CFWS in any - order as long as it starts with at least one word. If anything other than - words is detected, an ObsoleteHeaderDefect is added to the token's defect - list. We also accept a phrase that starts with CFWS followed by a dot; - this is registered as an InvalidHeaderDefect, since it is not supported by - even the obsolete grammar. - - """ - phrase = Phrase() - try: - token, value = get_word(value) - phrase.append(token) - except errors.HeaderParseError: - phrase.defects.append(errors.InvalidHeaderDefect( - "phrase does not start with word")) - while value and value[0] not in PHRASE_ENDS: - if value[0]=='.': - phrase.append(DOT) - phrase.defects.append(errors.ObsoleteHeaderDefect( - "period in 'phrase'")) - value = value[1:] - else: - try: - token, value = get_word(value) - except errors.HeaderParseError: - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - phrase.defects.append(errors.ObsoleteHeaderDefect( - "comment found without atom")) - else: - raise - phrase.append(token) - return phrase, value - -def get_local_part(value): - """ local-part = dot-atom / quoted-string / obs-local-part - - """ - local_part = LocalPart() - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected local-part but found '{}'".format(value)) - try: - token, value = get_dot_atom(value) - except errors.HeaderParseError: - try: - token, value = get_word(value) - except errors.HeaderParseError: - if value[0] != '\\' and value[0] in PHRASE_ENDS: - raise - token = TokenList() - if leader is not None: - token[:0] = [leader] - local_part.append(token) - if value and (value[0]=='\\' or value[0] not in PHRASE_ENDS): - obs_local_part, value = get_obs_local_part(str(local_part) + value) - if obs_local_part.token_type == 'invalid-obs-local-part': - local_part.defects.append(errors.InvalidHeaderDefect( - "local-part is not dot-atom, quoted-string, or obs-local-part")) - else: - local_part.defects.append(errors.ObsoleteHeaderDefect( - "local-part is not a dot-atom (contains CFWS)")) - local_part[0] = obs_local_part - try: - local_part.value.encode('ascii') - except UnicodeEncodeError: - local_part.defects.append(errors.NonASCIILocalPartDefect( - "local-part contains non-ASCII characters)")) - return local_part, value - -def get_obs_local_part(value): - """ obs-local-part = word *("." word) - """ - obs_local_part = ObsLocalPart() - last_non_ws_was_dot = False - while value and (value[0]=='\\' or value[0] not in PHRASE_ENDS): - if value[0] == '.': - if last_non_ws_was_dot: - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "invalid repeated '.'")) - obs_local_part.append(DOT) - last_non_ws_was_dot = True - value = value[1:] - continue - elif value[0]=='\\': - obs_local_part.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "'\\' character outside of quoted-string/ccontent")) - last_non_ws_was_dot = False - continue - if obs_local_part and obs_local_part[-1].token_type != 'dot': - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "missing '.' between words")) - try: - token, value = get_word(value) - last_non_ws_was_dot = False - except errors.HeaderParseError: - if value[0] not in CFWS_LEADER: - raise - token, value = get_cfws(value) - obs_local_part.append(token) - if (obs_local_part[0].token_type == 'dot' or - obs_local_part[0].token_type=='cfws' and - obs_local_part[1].token_type=='dot'): - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "Invalid leading '.' in local part")) - if (obs_local_part[-1].token_type == 'dot' or - obs_local_part[-1].token_type=='cfws' and - obs_local_part[-2].token_type=='dot'): - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "Invalid trailing '.' in local part")) - if obs_local_part.defects: - obs_local_part.token_type = 'invalid-obs-local-part' - return obs_local_part, value - -def get_dtext(value): - """ dtext = / obs-dtext - obs-dtext = obs-NO-WS-CTL / quoted-pair - - We allow anything except the excluded characters, but if we find any - ASCII other than the RFC defined printable ASCII an NonPrintableDefect is - added to the token's defects list. Quoted pairs are converted to their - unquoted values, so what is returned is a ptext token, in this case a - ValueTerminal. If there were quoted-printables, an ObsoleteHeaderDefect is - added to the returned token's defect list. - - """ - ptext, value, had_qp = _get_ptext_to_endchars(value, '[]') - ptext = ValueTerminal(ptext, 'ptext') - if had_qp: - ptext.defects.append(errors.ObsoleteHeaderDefect( - "quoted printable found in domain-literal")) - _validate_xtext(ptext) - return ptext, value - -def _check_for_early_dl_end(value, domain_literal): - if value: - return False - domain_literal.append(errors.InvalidHeaderDefect( - "end of input inside domain-literal")) - domain_literal.append(ValueTerminal(']', 'domain-literal-end')) - return True - -def get_domain_literal(value): - """ domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS] - - """ - domain_literal = DomainLiteral() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - domain_literal.append(token) - if not value: - raise errors.HeaderParseError("expected domain-literal") - if value[0] != '[': - raise errors.HeaderParseError("expected '[' at start of domain-literal " - "but found '{}'".format(value)) - value = value[1:] - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - domain_literal.append(ValueTerminal('[', 'domain-literal-start')) - if value[0] in WSP: - token, value = get_fws(value) - domain_literal.append(token) - token, value = get_dtext(value) - domain_literal.append(token) - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - if value[0] in WSP: - token, value = get_fws(value) - domain_literal.append(token) - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - if value[0] != ']': - raise errors.HeaderParseError("expected ']' at end of domain-literal " - "but found '{}'".format(value)) - domain_literal.append(ValueTerminal(']', 'domain-literal-end')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - domain_literal.append(token) - return domain_literal, value - -def get_domain(value): - """ domain = dot-atom / domain-literal / obs-domain - obs-domain = atom *("." atom)) - - """ - domain = Domain() - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected domain but found '{}'".format(value)) - if value[0] == '[': - token, value = get_domain_literal(value) - if leader is not None: - token[:0] = [leader] - domain.append(token) - return domain, value - try: - token, value = get_dot_atom(value) - except errors.HeaderParseError: - token, value = get_atom(value) - if leader is not None: - token[:0] = [leader] - domain.append(token) - if value and value[0] == '.': - domain.defects.append(errors.ObsoleteHeaderDefect( - "domain is not a dot-atom (contains CFWS)")) - if domain[0].token_type == 'dot-atom': - domain[:] = domain[0] - while value and value[0] == '.': - domain.append(DOT) - token, value = get_atom(value[1:]) - domain.append(token) - return domain, value - -def get_addr_spec(value): - """ addr-spec = local-part "@" domain - - """ - addr_spec = AddrSpec() - token, value = get_local_part(value) - addr_spec.append(token) - if not value or value[0] != '@': - addr_spec.defects.append(errors.InvalidHeaderDefect( - "add-spec local part with no domain")) - return addr_spec, value - addr_spec.append(ValueTerminal('@', 'address-at-symbol')) - token, value = get_domain(value[1:]) - addr_spec.append(token) - return addr_spec, value - -def get_obs_route(value): - """ obs-route = obs-domain-list ":" - obs-domain-list = *(CFWS / ",") "@" domain *("," [CFWS] ["@" domain]) - - Returns an obs-route token with the appropriate sub-tokens (that is, - there is no obs-domain-list in the parse tree). - """ - obs_route = ObsRoute() - while value and (value[0]==',' or value[0] in CFWS_LEADER): - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - obs_route.append(token) - elif value[0] == ',': - obs_route.append(ListSeparator) - value = value[1:] - if not value or value[0] != '@': - raise errors.HeaderParseError( - "expected obs-route domain but found '{}'".format(value)) - obs_route.append(RouteComponentMarker) - token, value = get_domain(value[1:]) - obs_route.append(token) - while value and value[0]==',': - obs_route.append(ListSeparator) - value = value[1:] - if not value: - break - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - obs_route.append(token) - if value[0] == '@': - obs_route.append(RouteComponentMarker) - token, value = get_domain(value[1:]) - obs_route.append(token) - if not value: - raise errors.HeaderParseError("end of header while parsing obs-route") - if value[0] != ':': - raise errors.HeaderParseError( "expected ':' marking end of " - "obs-route but found '{}'".format(value)) - obs_route.append(ValueTerminal(':', 'end-of-obs-route-marker')) - return obs_route, value[1:] - -def get_angle_addr(value): - """ angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr - obs-angle-addr = [CFWS] "<" obs-route addr-spec ">" [CFWS] - - """ - angle_addr = AngleAddr() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - angle_addr.append(token) - if not value or value[0] != '<': - raise errors.HeaderParseError( - "expected angle-addr but found '{}'".format(value)) - angle_addr.append(ValueTerminal('<', 'angle-addr-start')) - value = value[1:] - # Although it is not legal per RFC5322, SMTP uses '<>' in certain - # circumstances. - if value[0] == '>': - angle_addr.append(ValueTerminal('>', 'angle-addr-end')) - angle_addr.defects.append(errors.InvalidHeaderDefect( - "null addr-spec in angle-addr")) - value = value[1:] - return angle_addr, value - try: - token, value = get_addr_spec(value) - except errors.HeaderParseError: - try: - token, value = get_obs_route(value) - angle_addr.defects.append(errors.ObsoleteHeaderDefect( - "obsolete route specification in angle-addr")) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected addr-spec or obs-route but found '{}'".format(value)) - angle_addr.append(token) - token, value = get_addr_spec(value) - angle_addr.append(token) - if value and value[0] == '>': - value = value[1:] - else: - angle_addr.defects.append(errors.InvalidHeaderDefect( - "missing trailing '>' on angle-addr")) - angle_addr.append(ValueTerminal('>', 'angle-addr-end')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - angle_addr.append(token) - return angle_addr, value - -def get_display_name(value): - """ display-name = phrase - - Because this is simply a name-rule, we don't return a display-name - token containing a phrase, but rather a display-name token with - the content of the phrase. - - """ - display_name = DisplayName() - token, value = get_phrase(value) - display_name.extend(token[:]) - display_name.defects = token.defects[:] - return display_name, value - - -def get_name_addr(value): - """ name-addr = [display-name] angle-addr - - """ - name_addr = NameAddr() - # Both the optional display name and the angle-addr can start with cfws. - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(leader)) - if value[0] != '<': - if value[0] in PHRASE_ENDS: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(value)) - token, value = get_display_name(value) - if not value: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(token)) - if leader is not None: - token[0][:0] = [leader] - leader = None - name_addr.append(token) - token, value = get_angle_addr(value) - if leader is not None: - token[:0] = [leader] - name_addr.append(token) - return name_addr, value - -def get_mailbox(value): - """ mailbox = name-addr / addr-spec - - """ - # The only way to figure out if we are dealing with a name-addr or an - # addr-spec is to try parsing each one. - mailbox = Mailbox() - try: - token, value = get_name_addr(value) - except errors.HeaderParseError: - try: - token, value = get_addr_spec(value) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected mailbox but found '{}'".format(value)) - if any(isinstance(x, errors.InvalidHeaderDefect) - for x in token.all_defects): - mailbox.token_type = 'invalid-mailbox' - mailbox.append(token) - return mailbox, value - -def get_invalid_mailbox(value, endchars): - """ Read everything up to one of the chars in endchars. - - This is outside the formal grammar. The InvalidMailbox TokenList that is - returned acts like a Mailbox, but the data attributes are None. - - """ - invalid_mailbox = InvalidMailbox() - while value and value[0] not in endchars: - if value[0] in PHRASE_ENDS: - invalid_mailbox.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - invalid_mailbox.append(token) - return invalid_mailbox, value - -def get_mailbox_list(value): - """ mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list - obs-mbox-list = *([CFWS] ",") mailbox *("," [mailbox / CFWS]) - - For this routine we go outside the formal grammar in order to improve error - handling. We recognize the end of the mailbox list only at the end of the - value or at a ';' (the group terminator). This is so that we can turn - invalid mailboxes into InvalidMailbox tokens and continue parsing any - remaining valid mailboxes. We also allow all mailbox entries to be null, - and this condition is handled appropriately at a higher level. - - """ - mailbox_list = MailboxList() - while value and value[0] != ';': - try: - token, value = get_mailbox(value) - mailbox_list.append(token) - except errors.HeaderParseError: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value or value[0] in ',;': - mailbox_list.append(leader) - mailbox_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in mailbox-list")) - else: - token, value = get_invalid_mailbox(value, ',;') - if leader is not None: - token[:0] = [leader] - mailbox_list.append(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - elif value[0] == ',': - mailbox_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in mailbox-list")) - else: - token, value = get_invalid_mailbox(value, ',;') - if leader is not None: - token[:0] = [leader] - mailbox_list.append(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - if value and value[0] not in ',;': - # Crap after mailbox; treat it as an invalid mailbox. - # The mailbox info will still be available. - mailbox = mailbox_list[-1] - mailbox.token_type = 'invalid-mailbox' - token, value = get_invalid_mailbox(value, ',;') - mailbox.extend(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - if value and value[0] == ',': - mailbox_list.append(ListSeparator) - value = value[1:] - return mailbox_list, value - - -def get_group_list(value): - """ group-list = mailbox-list / CFWS / obs-group-list - obs-group-list = 1*([CFWS] ",") [CFWS] - - """ - group_list = GroupList() - if not value: - group_list.defects.append(errors.InvalidHeaderDefect( - "end of header before group-list")) - return group_list, value - leader = None - if value and value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - # This should never happen in email parsing, since CFWS-only is a - # legal alternative to group-list in a group, which is the only - # place group-list appears. - group_list.defects.append(errors.InvalidHeaderDefect( - "end of header in group-list")) - group_list.append(leader) - return group_list, value - if value[0] == ';': - group_list.append(leader) - return group_list, value - token, value = get_mailbox_list(value) - if len(token.all_mailboxes)==0: - if leader is not None: - group_list.append(leader) - group_list.extend(token) - group_list.defects.append(errors.ObsoleteHeaderDefect( - "group-list with empty entries")) - return group_list, value - if leader is not None: - token[:0] = [leader] - group_list.append(token) - return group_list, value - -def get_group(value): - """ group = display-name ":" [group-list] ";" [CFWS] - - """ - group = Group() - token, value = get_display_name(value) - if not value or value[0] != ':': - raise errors.HeaderParseError("expected ':' at end of group " - "display name but found '{}'".format(value)) - group.append(token) - group.append(ValueTerminal(':', 'group-display-name-terminator')) - value = value[1:] - if value and value[0] == ';': - group.append(ValueTerminal(';', 'group-terminator')) - return group, value[1:] - token, value = get_group_list(value) - group.append(token) - if not value: - group.defects.append(errors.InvalidHeaderDefect( - "end of header in group")) - if value[0] != ';': - raise errors.HeaderParseError( - "expected ';' at end of group but found {}".format(value)) - group.append(ValueTerminal(';', 'group-terminator')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - group.append(token) - return group, value - -def get_address(value): - """ address = mailbox / group - - Note that counter-intuitively, an address can be either a single address or - a list of addresses (a group). This is why the returned Address object has - a 'mailboxes' attribute which treats a single address as a list of length - one. When you need to differentiate between to two cases, extract the single - element, which is either a mailbox or a group token. - - """ - # The formal grammar isn't very helpful when parsing an address. mailbox - # and group, especially when allowing for obsolete forms, start off very - # similarly. It is only when you reach one of @, <, or : that you know - # what you've got. So, we try each one in turn, starting with the more - # likely of the two. We could perhaps make this more efficient by looking - # for a phrase and then branching based on the next character, but that - # would be a premature optimization. - address = Address() - try: - token, value = get_group(value) - except errors.HeaderParseError: - try: - token, value = get_mailbox(value) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected address but found '{}'".format(value)) - address.append(token) - return address, value - -def get_address_list(value): - """ address_list = (address *("," address)) / obs-addr-list - obs-addr-list = *([CFWS] ",") address *("," [address / CFWS]) - - We depart from the formal grammar here by continuing to parse until the end - of the input, assuming the input to be entirely composed of an - address-list. This is always true in email parsing, and allows us - to skip invalid addresses to parse additional valid ones. - - """ - address_list = AddressList() - while value: - try: - token, value = get_address(value) - address_list.append(token) - except errors.HeaderParseError as err: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value or value[0] == ',': - address_list.append(leader) - address_list.defects.append(errors.ObsoleteHeaderDefect( - "address-list entry with no content")) - else: - token, value = get_invalid_mailbox(value, ',') - if leader is not None: - token[:0] = [leader] - address_list.append(Address([token])) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - elif value[0] == ',': - address_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in address-list")) - else: - token, value = get_invalid_mailbox(value, ',') - if leader is not None: - token[:0] = [leader] - address_list.append(Address([token])) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - if value and value[0] != ',': - # Crap after address; treat it as an invalid mailbox. - # The mailbox info will still be available. - mailbox = address_list[-1][0] - mailbox.token_type = 'invalid-mailbox' - token, value = get_invalid_mailbox(value, ',') - mailbox.extend(token) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - if value: # Must be a , at this point. - address_list.append(ValueTerminal(',', 'list-separator')) - value = value[1:] - return address_list, value - -# -# XXX: As I begin to add additional header parsers, I'm realizing we probably -# have two level of parser routines: the get_XXX methods that get a token in -# the grammar, and parse_XXX methods that parse an entire field value. So -# get_address_list above should really be a parse_ method, as probably should -# be get_unstructured. -# - -def parse_mime_version(value): - """ mime-version = [CFWS] 1*digit [CFWS] "." [CFWS] 1*digit [CFWS] - - """ - # The [CFWS] is implicit in the RFC 2045 BNF. - # XXX: This routine is a bit verbose, should factor out a get_int method. - mime_version = MIMEVersion() - if not value: - mime_version.defects.append(errors.HeaderMissingRequiredValue( - "Missing MIME version number (eg: 1.0)")) - return mime_version - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value: - mime_version.defects.append(errors.HeaderMissingRequiredValue( - "Expected MIME version number but found only CFWS")) - digits = '' - while value and value[0] != '.' and value[0] not in CFWS_LEADER: - digits += value[0] - value = value[1:] - if not digits.isdigit(): - mime_version.defects.append(errors.InvalidHeaderDefect( - "Expected MIME major version number but found {!r}".format(digits))) - mime_version.append(ValueTerminal(digits, 'xtext')) - else: - mime_version.major = int(digits) - mime_version.append(ValueTerminal(digits, 'digits')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value or value[0] != '.': - if mime_version.major is not None: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Incomplete MIME version; found only major number")) - if value: - mime_version.append(ValueTerminal(value, 'xtext')) - return mime_version - mime_version.append(ValueTerminal('.', 'version-separator')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value: - if mime_version.major is not None: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Incomplete MIME version; found only major number")) - return mime_version - digits = '' - while value and value[0] not in CFWS_LEADER: - digits += value[0] - value = value[1:] - if not digits.isdigit(): - mime_version.defects.append(errors.InvalidHeaderDefect( - "Expected MIME minor version number but found {!r}".format(digits))) - mime_version.append(ValueTerminal(digits, 'xtext')) - else: - mime_version.minor = int(digits) - mime_version.append(ValueTerminal(digits, 'digits')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if value: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Excess non-CFWS text after MIME version")) - mime_version.append(ValueTerminal(value, 'xtext')) - return mime_version - -def get_invalid_parameter(value): - """ Read everything up to the next ';'. - - This is outside the formal grammar. The InvalidParameter TokenList that is - returned acts like a Parameter, but the data attributes are None. - - """ - invalid_parameter = InvalidParameter() - while value and value[0] != ';': - if value[0] in PHRASE_ENDS: - invalid_parameter.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - invalid_parameter.append(token) - return invalid_parameter, value - -def get_ttext(value): - """ttext = - - We allow any non-TOKEN_ENDS in ttext, but add defects to the token's - defects list if we find non-ttext characters. We also register defects for - *any* non-printables even though the RFC doesn't exclude all of them, - because we follow the spirit of RFC 5322. - - """ - m = _non_token_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected ttext but found '{}'".format(value)) - ttext = m.group() - value = value[len(ttext):] - ttext = ValueTerminal(ttext, 'ttext') - _validate_xtext(ttext) - return ttext, value - -def get_token(value): - """token = [CFWS] 1*ttext [CFWS] - - The RFC equivalent of ttext is any US-ASCII chars except space, ctls, or - tspecials. We also exclude tabs even though the RFC doesn't. - - The RFC implies the CFWS but is not explicit about it in the BNF. - - """ - mtoken = Token() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mtoken.append(token) - if value and value[0] in TOKEN_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_ttext(value) - mtoken.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mtoken.append(token) - return mtoken, value - -def get_attrtext(value): - """attrtext = 1*(any non-ATTRIBUTE_ENDS character) - - We allow any non-ATTRIBUTE_ENDS in attrtext, but add defects to the - token's defects list if we find non-attrtext characters. We also register - defects for *any* non-printables even though the RFC doesn't exclude all of - them, because we follow the spirit of RFC 5322. - - """ - m = _non_attribute_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected attrtext but found {!r}".format(value)) - attrtext = m.group() - value = value[len(attrtext):] - attrtext = ValueTerminal(attrtext, 'attrtext') - _validate_xtext(attrtext) - return attrtext, value - -def get_attribute(value): - """ [CFWS] 1*attrtext [CFWS] - - This version of the BNF makes the CFWS explicit, and as usual we use a - value terminal for the actual run of characters. The RFC equivalent of - attrtext is the token characters, with the subtraction of '*', "'", and '%'. - We include tab in the excluded set just as we do for token. - - """ - attribute = Attribute() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - if value and value[0] in ATTRIBUTE_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_attrtext(value) - attribute.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - return attribute, value - -def get_extended_attrtext(value): - """attrtext = 1*(any non-ATTRIBUTE_ENDS character plus '%') - - This is a special parsing routine so that we get a value that - includes % escapes as a single string (which we decode as a single - string later). - - """ - m = _non_extended_attribute_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected extended attrtext but found {!r}".format(value)) - attrtext = m.group() - value = value[len(attrtext):] - attrtext = ValueTerminal(attrtext, 'extended-attrtext') - _validate_xtext(attrtext) - return attrtext, value - -def get_extended_attribute(value): - """ [CFWS] 1*extended_attrtext [CFWS] - - This is like the non-extended version except we allow % characters, so that - we can pick up an encoded value as a single string. - - """ - # XXX: should we have an ExtendedAttribute TokenList? - attribute = Attribute() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - if value and value[0] in EXTENDED_ATTRIBUTE_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_extended_attrtext(value) - attribute.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - return attribute, value - -def get_section(value): - """ '*' digits - - The formal BNF is more complicated because leading 0s are not allowed. We - check for that and add a defect. We also assume no CFWS is allowed between - the '*' and the digits, though the RFC is not crystal clear on that. - The caller should already have dealt with leading CFWS. - - """ - section = Section() - if not value or value[0] != '*': - raise errors.HeaderParseError("Expected section but found {}".format( - value)) - section.append(ValueTerminal('*', 'section-marker')) - value = value[1:] - if not value or not value[0].isdigit(): - raise errors.HeaderParseError("Expected section number but " - "found {}".format(value)) - digits = '' - while value and value[0].isdigit(): - digits += value[0] - value = value[1:] - if digits[0] == '0' and digits != '0': - section.defects.append(errors.InvalidHeaderError("section number" - "has an invalid leading 0")) - section.number = int(digits) - section.append(ValueTerminal(digits, 'digits')) - return section, value - - -def get_value(value): - """ quoted-string / attribute - - """ - v = Value() - if not value: - raise errors.HeaderParseError("Expected value but found end of string") - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError("Expected value but found " - "only {}".format(leader)) - if value[0] == '"': - token, value = get_quoted_string(value) - else: - token, value = get_extended_attribute(value) - if leader is not None: - token[:0] = [leader] - v.append(token) - return v, value - -def get_parameter(value): - """ attribute [section] ["*"] [CFWS] "=" value - - The CFWS is implied by the RFC but not made explicit in the BNF. This - simplified form of the BNF from the RFC is made to conform with the RFC BNF - through some extra checks. We do it this way because it makes both error - recovery and working with the resulting parse tree easier. - """ - # It is possible CFWS would also be implicitly allowed between the section - # and the 'extended-attribute' marker (the '*') , but we've never seen that - # in the wild and we will therefore ignore the possibility. - param = Parameter() - token, value = get_attribute(value) - param.append(token) - if not value or value[0] == ';': - param.defects.append(errors.InvalidHeaderDefect("Parameter contains " - "name ({}) but no value".format(token))) - return param, value - if value[0] == '*': - try: - token, value = get_section(value) - param.sectioned = True - param.append(token) - except errors.HeaderParseError: - pass - if not value: - raise errors.HeaderParseError("Incomplete parameter") - if value[0] == '*': - param.append(ValueTerminal('*', 'extended-parameter-marker')) - value = value[1:] - param.extended = True - if value[0] != '=': - raise errors.HeaderParseError("Parameter not followed by '='") - param.append(ValueTerminal('=', 'parameter-separator')) - value = value[1:] - leader = None - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - param.append(token) - remainder = None - appendto = param - if param.extended and value and value[0] == '"': - # Now for some serious hackery to handle the common invalid case of - # double quotes around an extended value. We also accept (with defect) - # a value marked as encoded that isn't really. - qstring, remainder = get_quoted_string(value) - inner_value = qstring.stripped_value - semi_valid = False - if param.section_number == 0: - if inner_value and inner_value[0] == "'": - semi_valid = True - else: - token, rest = get_attrtext(inner_value) - if rest and rest[0] == "'": - semi_valid = True - else: - try: - token, rest = get_extended_attrtext(inner_value) - except: - pass - else: - if not rest: - semi_valid = True - if semi_valid: - param.defects.append(errors.InvalidHeaderDefect( - "Quoted string value for extended parameter is invalid")) - param.append(qstring) - for t in qstring: - if t.token_type == 'bare-quoted-string': - t[:] = [] - appendto = t - break - value = inner_value - else: - remainder = None - param.defects.append(errors.InvalidHeaderDefect( - "Parameter marked as extended but appears to have a " - "quoted string value that is non-encoded")) - if value and value[0] == "'": - token = None - else: - token, value = get_value(value) - if not param.extended or param.section_number > 0: - if not value or value[0] != "'": - appendto.append(token) - if remainder is not None: - assert not value, value - value = remainder - return param, value - param.defects.append(errors.InvalidHeaderDefect( - "Apparent initial-extended-value but attribute " - "was not marked as extended or was not initial section")) - if not value: - # Assume the charset/lang is missing and the token is the value. - param.defects.append(errors.InvalidHeaderDefect( - "Missing required charset/lang delimiters")) - appendto.append(token) - if remainder is None: - return param, value - else: - if token is not None: - for t in token: - if t.token_type == 'extended-attrtext': - break - t.token_type == 'attrtext' - appendto.append(t) - param.charset = t.value - if value[0] != "'": - raise errors.HeaderParseError("Expected RFC2231 char/lang encoding " - "delimiter, but found {!r}".format(value)) - appendto.append(ValueTerminal("'", 'RFC2231 delimiter')) - value = value[1:] - if value and value[0] != "'": - token, value = get_attrtext(value) - appendto.append(token) - param.lang = token.value - if not value or value[0] != "'": - raise errors.HeaderParseError("Expected RFC2231 char/lang encoding " - "delimiter, but found {}".format(value)) - appendto.append(ValueTerminal("'", 'RFC2231 delimiter')) - value = value[1:] - if remainder is not None: - # Treat the rest of value as bare quoted string content. - v = Value() - while value: - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_qcontent(value) - v.append(token) - token = v - else: - token, value = get_value(value) - appendto.append(token) - if remainder is not None: - assert not value, value - value = remainder - return param, value - -def parse_mime_parameters(value): - """ parameter *( ";" parameter ) - - That BNF is meant to indicate this routine should only be called after - finding and handling the leading ';'. There is no corresponding rule in - the formal RFC grammar, but it is more convenient for us for the set of - parameters to be treated as its own TokenList. - - This is 'parse' routine because it consumes the reminaing value, but it - would never be called to parse a full header. Instead it is called to - parse everything after the non-parameter value of a specific MIME header. - - """ - mime_parameters = MimeParameters() - while value: - try: - token, value = get_parameter(value) - mime_parameters.append(token) - except errors.HeaderParseError as err: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - mime_parameters.append(leader) - return mime_parameters - if value[0] == ';': - if leader is not None: - mime_parameters.append(leader) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "parameter entry with no content")) - else: - token, value = get_invalid_parameter(value) - if leader: - token[:0] = [leader] - mime_parameters.append(token) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "invalid parameter {!r}".format(token))) - if value and value[0] != ';': - # Junk after the otherwise valid parameter. Mark it as - # invalid, but it will have a value. - param = mime_parameters[-1] - param.token_type = 'invalid-parameter' - token, value = get_invalid_parameter(value) - param.extend(token) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "parameter with invalid trailing text {!r}".format(token))) - if value: - # Must be a ';' at this point. - mime_parameters.append(ValueTerminal(';', 'parameter-separator')) - value = value[1:] - return mime_parameters - -def _find_mime_parameters(tokenlist, value): - """Do our best to find the parameters in an invalid MIME header - - """ - while value and value[0] != ';': - if value[0] in PHRASE_ENDS: - tokenlist.append(ValueTerminal(value[0], 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - tokenlist.append(token) - if not value: - return - tokenlist.append(ValueTerminal(';', 'parameter-separator')) - tokenlist.append(parse_mime_parameters(value[1:])) - -def parse_content_type_header(value): - """ maintype "/" subtype *( ";" parameter ) - - The maintype and substype are tokens. Theoretically they could - be checked against the official IANA list + x-token, but we - don't do that. - """ - ctype = ContentType() - recover = False - if not value: - ctype.defects.append(errors.HeaderMissingRequiredValue( - "Missing content type specification")) - return ctype - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content maintype but found {!r}".format(value))) - _find_mime_parameters(ctype, value) - return ctype - ctype.append(token) - # XXX: If we really want to follow the formal grammer we should make - # mantype and subtype specialized TokenLists here. Probably not worth it. - if not value or value[0] != '/': - ctype.defects.append(errors.InvalidHeaderDefect( - "Invalid content type")) - if value: - _find_mime_parameters(ctype, value) - return ctype - ctype.maintype = token.value.strip().lower() - ctype.append(ValueTerminal('/', 'content-type-separator')) - value = value[1:] - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content subtype but found {!r}".format(value))) - _find_mime_parameters(ctype, value) - return ctype - ctype.append(token) - ctype.subtype = token.value.strip().lower() - if not value: - return ctype - if value[0] != ';': - ctype.defects.append(errors.InvalidHeaderDefect( - "Only parameters are valid after content type, but " - "found {!r}".format(value))) - # The RFC requires that a syntactically invalid content-type be treated - # as text/plain. Perhaps we should postel this, but we should probably - # only do that if we were checking the subtype value against IANA. - del ctype.maintype, ctype.subtype - _find_mime_parameters(ctype, value) - return ctype - ctype.append(ValueTerminal(';', 'parameter-separator')) - ctype.append(parse_mime_parameters(value[1:])) - return ctype - -def parse_content_disposition_header(value): - """ disposition-type *( ";" parameter ) - - """ - disp_header = ContentDisposition() - if not value: - disp_header.defects.append(errors.HeaderMissingRequiredValue( - "Missing content disposition")) - return disp_header - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content disposition but found {!r}".format(value))) - _find_mime_parameters(disp_header, value) - return disp_header - disp_header.append(token) - disp_header.content_disposition = token.value.strip().lower() - if not value: - return disp_header - if value[0] != ';': - disp_header.defects.append(errors.InvalidHeaderDefect( - "Only parameters are valid after content disposition, but " - "found {!r}".format(value))) - _find_mime_parameters(disp_header, value) - return disp_header - disp_header.append(ValueTerminal(';', 'parameter-separator')) - disp_header.append(parse_mime_parameters(value[1:])) - return disp_header - -def parse_content_transfer_encoding_header(value): - """ mechanism - - """ - # We should probably validate the values, since the list is fixed. - cte_header = ContentTransferEncoding() - if not value: - cte_header.defects.append(errors.HeaderMissingRequiredValue( - "Missing content transfer encoding")) - return cte_header - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content trnasfer encoding but found {!r}".format(value))) - else: - cte_header.append(token) - cte_header.cte = token.value.strip().lower() - if not value: - return cte_header - while value: - cte_header.defects.append(errors.InvalidHeaderDefect( - "Extra text after content transfer encoding")) - if value[0] in PHRASE_ENDS: - cte_header.append(ValueTerminal(value[0], 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - cte_header.append(token) - return cte_header diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -""" -Backport of the Python 3.3 email package for Python-Future. - -A package for parsing, handling, and generating email messages. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -# Install the surrogate escape handler here because this is used by many -# modules in the email package. -from future.utils import surrogateescape -surrogateescape.register_surrogateescape() -# (Should this be done globally by ``future``?) - - -__version__ = '5.1.0' - -__all__ = [ - 'base64mime', - 'charset', - 'encoders', - 'errors', - 'feedparser', - 'generator', - 'header', - 'iterators', - 'message', - 'message_from_file', - 'message_from_binary_file', - 'message_from_string', - 'message_from_bytes', - 'mime', - 'parser', - 'quoprimime', - 'utils', - ] - - - -# Some convenience routines. Don't import Parser and Message as side-effects -# of importing email since those cascadingly import most of the rest of the -# email package. -def message_from_string(s, *args, **kws): - """Parse a string into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import Parser - return Parser(*args, **kws).parsestr(s) - -def message_from_bytes(s, *args, **kws): - """Parse a bytes string into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import BytesParser - return BytesParser(*args, **kws).parsebytes(s) - -def message_from_file(fp, *args, **kws): - """Read a file and parse its contents into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import Parser - return Parser(*args, **kws).parse(fp) - -def message_from_binary_file(fp, *args, **kws): - """Read a binary file and parse its contents into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import BytesParser - return BytesParser(*args, **kws).parse(fp) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/iterators.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/iterators.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/iterators.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/iterators.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Various types of useful iterators and generators.""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = [ - 'body_line_iterator', - 'typed_subpart_iterator', - 'walk', - # Do not include _structure() since it's part of the debugging API. - ] - -import sys -from io import StringIO - - -# This function will become a method of the Message class -def walk(self): - """Walk over the message tree, yielding each subpart. - - The walk is performed in depth-first order. This method is a - generator. - """ - yield self - if self.is_multipart(): - for subpart in self.get_payload(): - for subsubpart in subpart.walk(): - yield subsubpart - - -# These two functions are imported into the Iterators.py interface module. -def body_line_iterator(msg, decode=False): - """Iterate over the parts, returning string payloads line-by-line. - - Optional decode (default False) is passed through to .get_payload(). - """ - for subpart in msg.walk(): - payload = subpart.get_payload(decode=decode) - if isinstance(payload, str): - for line in StringIO(payload): - yield line - - -def typed_subpart_iterator(msg, maintype='text', subtype=None): - """Iterate over the subparts with a given MIME type. - - Use `maintype' as the main MIME type to match against; this defaults to - "text". Optional `subtype' is the MIME subtype to match against; if - omitted, only the main type is matched. - """ - for subpart in msg.walk(): - if subpart.get_content_maintype() == maintype: - if subtype is None or subpart.get_content_subtype() == subtype: - yield subpart - - -def _structure(msg, fp=None, level=0, include_default=False): - """A handy debugging aid""" - if fp is None: - fp = sys.stdout - tab = ' ' * (level * 4) - print(tab + msg.get_content_type(), end='', file=fp) - if include_default: - print(' [%s]' % msg.get_default_type(), file=fp) - else: - print(file=fp) - if msg.is_multipart(): - for subpart in msg.get_payload(): - _structure(subpart, fp, level+1, include_default) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/message.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/message.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/message.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/message.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,882 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Basic message object for the email package object model.""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import list, range, str, zip - -__all__ = ['Message'] - -import re -import uu -import base64 -import binascii -from io import BytesIO, StringIO - -# Intrapackage imports -from future.utils import as_native_str -from future.backports.email import utils -from future.backports.email import errors -from future.backports.email._policybase import compat32 -from future.backports.email import charset as _charset -from future.backports.email._encoded_words import decode_b -Charset = _charset.Charset - -SEMISPACE = '; ' - -# Regular expression that matches `special' characters in parameters, the -# existence of which force quoting of the parameter value. -tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') - - -def _splitparam(param): - # Split header parameters. BAW: this may be too simple. It isn't - # strictly RFC 2045 (section 5.1) compliant, but it catches most headers - # found in the wild. We may eventually need a full fledged parser. - # RDM: we might have a Header here; for now just stringify it. - a, sep, b = str(param).partition(';') - if not sep: - return a.strip(), None - return a.strip(), b.strip() - -def _formatparam(param, value=None, quote=True): - """Convenience function to format and return a key=value pair. - - This will quote the value if needed or if quote is true. If value is a - three tuple (charset, language, value), it will be encoded according - to RFC2231 rules. If it contains non-ascii characters it will likewise - be encoded according to RFC2231 rules, using the utf-8 charset and - a null language. - """ - if value is not None and len(value) > 0: - # A tuple is used for RFC 2231 encoded parameter values where items - # are (charset, language, value). charset is a string, not a Charset - # instance. RFC 2231 encoded values are never quoted, per RFC. - if isinstance(value, tuple): - # Encode as per RFC 2231 - param += '*' - value = utils.encode_rfc2231(value[2], value[0], value[1]) - return '%s=%s' % (param, value) - else: - try: - value.encode('ascii') - except UnicodeEncodeError: - param += '*' - value = utils.encode_rfc2231(value, 'utf-8', '') - return '%s=%s' % (param, value) - # BAW: Please check this. I think that if quote is set it should - # force quoting even if not necessary. - if quote or tspecials.search(value): - return '%s="%s"' % (param, utils.quote(value)) - else: - return '%s=%s' % (param, value) - else: - return param - -def _parseparam(s): - # RDM This might be a Header, so for now stringify it. - s = ';' + str(s) - plist = [] - while s[:1] == ';': - s = s[1:] - end = s.find(';') - while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2: - end = s.find(';', end + 1) - if end < 0: - end = len(s) - f = s[:end] - if '=' in f: - i = f.index('=') - f = f[:i].strip().lower() + '=' + f[i+1:].strip() - plist.append(f.strip()) - s = s[end:] - return plist - - -def _unquotevalue(value): - # This is different than utils.collapse_rfc2231_value() because it doesn't - # try to convert the value to a unicode. Message.get_param() and - # Message.get_params() are both currently defined to return the tuple in - # the face of RFC 2231 parameters. - if isinstance(value, tuple): - return value[0], value[1], utils.unquote(value[2]) - else: - return utils.unquote(value) - - -class Message(object): - """Basic message object. - - A message object is defined as something that has a bunch of RFC 2822 - headers and a payload. It may optionally have an envelope header - (a.k.a. Unix-From or From_ header). If the message is a container (i.e. a - multipart or a message/rfc822), then the payload is a list of Message - objects, otherwise it is a string. - - Message objects implement part of the `mapping' interface, which assumes - there is exactly one occurrence of the header per message. Some headers - do in fact appear multiple times (e.g. Received) and for those headers, - you must use the explicit API to set or get all the headers. Not all of - the mapping methods are implemented. - """ - def __init__(self, policy=compat32): - self.policy = policy - self._headers = list() - self._unixfrom = None - self._payload = None - self._charset = None - # Defaults for multipart messages - self.preamble = self.epilogue = None - self.defects = [] - # Default content type - self._default_type = 'text/plain' - - @as_native_str(encoding='utf-8') - def __str__(self): - """Return the entire formatted message as a string. - This includes the headers, body, and envelope header. - """ - return self.as_string() - - def as_string(self, unixfrom=False, maxheaderlen=0): - """Return the entire formatted message as a (unicode) string. - Optional `unixfrom' when True, means include the Unix From_ envelope - header. - - This is a convenience method and may not generate the message exactly - as you intend. For more flexibility, use the flatten() method of a - Generator instance. - """ - from future.backports.email.generator import Generator - fp = StringIO() - g = Generator(fp, mangle_from_=False, maxheaderlen=maxheaderlen) - g.flatten(self, unixfrom=unixfrom) - return fp.getvalue() - - def is_multipart(self): - """Return True if the message consists of multiple parts.""" - return isinstance(self._payload, list) - - # - # Unix From_ line - # - def set_unixfrom(self, unixfrom): - self._unixfrom = unixfrom - - def get_unixfrom(self): - return self._unixfrom - - # - # Payload manipulation. - # - def attach(self, payload): - """Add the given payload to the current payload. - - The current payload will always be a list of objects after this method - is called. If you want to set the payload to a scalar object, use - set_payload() instead. - """ - if self._payload is None: - self._payload = [payload] - else: - self._payload.append(payload) - - def get_payload(self, i=None, decode=False): - """Return a reference to the payload. - - The payload will either be a list object or a string. If you mutate - the list object, you modify the message's payload in place. Optional - i returns that index into the payload. - - Optional decode is a flag indicating whether the payload should be - decoded or not, according to the Content-Transfer-Encoding header - (default is False). - - When True and the message is not a multipart, the payload will be - decoded if this header's value is `quoted-printable' or `base64'. If - some other encoding is used, or the header is missing, or if the - payload has bogus data (i.e. bogus base64 or uuencoded data), the - payload is returned as-is. - - If the message is a multipart and the decode flag is True, then None - is returned. - """ - # Here is the logic table for this code, based on the email5.0.0 code: - # i decode is_multipart result - # ------ ------ ------------ ------------------------------ - # None True True None - # i True True None - # None False True _payload (a list) - # i False True _payload element i (a Message) - # i False False error (not a list) - # i True False error (not a list) - # None False False _payload - # None True False _payload decoded (bytes) - # Note that Barry planned to factor out the 'decode' case, but that - # isn't so easy now that we handle the 8 bit data, which needs to be - # converted in both the decode and non-decode path. - if self.is_multipart(): - if decode: - return None - if i is None: - return self._payload - else: - return self._payload[i] - # For backward compatibility, Use isinstance and this error message - # instead of the more logical is_multipart test. - if i is not None and not isinstance(self._payload, list): - raise TypeError('Expected list, got %s' % type(self._payload)) - payload = self._payload - # cte might be a Header, so for now stringify it. - cte = str(self.get('content-transfer-encoding', '')).lower() - # payload may be bytes here. - if isinstance(payload, str): - payload = str(payload) # for Python-Future, so surrogateescape works - if utils._has_surrogates(payload): - bpayload = payload.encode('ascii', 'surrogateescape') - if not decode: - try: - payload = bpayload.decode(self.get_param('charset', 'ascii'), 'replace') - except LookupError: - payload = bpayload.decode('ascii', 'replace') - elif decode: - try: - bpayload = payload.encode('ascii') - except UnicodeError: - # This won't happen for RFC compliant messages (messages - # containing only ASCII codepoints in the unicode input). - # If it does happen, turn the string into bytes in a way - # guaranteed not to fail. - bpayload = payload.encode('raw-unicode-escape') - if not decode: - return payload - if cte == 'quoted-printable': - return utils._qdecode(bpayload) - elif cte == 'base64': - # XXX: this is a bit of a hack; decode_b should probably be factored - # out somewhere, but I haven't figured out where yet. - value, defects = decode_b(b''.join(bpayload.splitlines())) - for defect in defects: - self.policy.handle_defect(self, defect) - return value - elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): - in_file = BytesIO(bpayload) - out_file = BytesIO() - try: - uu.decode(in_file, out_file, quiet=True) - return out_file.getvalue() - except uu.Error: - # Some decoding problem - return bpayload - if isinstance(payload, str): - return bpayload - return payload - - def set_payload(self, payload, charset=None): - """Set the payload to the given value. - - Optional charset sets the message's default character set. See - set_charset() for details. - """ - self._payload = payload - if charset is not None: - self.set_charset(charset) - - def set_charset(self, charset): - """Set the charset of the payload to a given character set. - - charset can be a Charset instance, a string naming a character set, or - None. If it is a string it will be converted to a Charset instance. - If charset is None, the charset parameter will be removed from the - Content-Type field. Anything else will generate a TypeError. - - The message will be assumed to be of type text/* encoded with - charset.input_charset. It will be converted to charset.output_charset - and encoded properly, if needed, when generating the plain text - representation of the message. MIME headers (MIME-Version, - Content-Type, Content-Transfer-Encoding) will be added as needed. - """ - if charset is None: - self.del_param('charset') - self._charset = None - return - if not isinstance(charset, Charset): - charset = Charset(charset) - self._charset = charset - if 'MIME-Version' not in self: - self.add_header('MIME-Version', '1.0') - if 'Content-Type' not in self: - self.add_header('Content-Type', 'text/plain', - charset=charset.get_output_charset()) - else: - self.set_param('charset', charset.get_output_charset()) - if charset != charset.get_output_charset(): - self._payload = charset.body_encode(self._payload) - if 'Content-Transfer-Encoding' not in self: - cte = charset.get_body_encoding() - try: - cte(self) - except TypeError: - self._payload = charset.body_encode(self._payload) - self.add_header('Content-Transfer-Encoding', cte) - - def get_charset(self): - """Return the Charset instance associated with the message's payload. - """ - return self._charset - - # - # MAPPING INTERFACE (partial) - # - def __len__(self): - """Return the total number of headers, including duplicates.""" - return len(self._headers) - - def __getitem__(self, name): - """Get a header value. - - Return None if the header is missing instead of raising an exception. - - Note that if the header appeared multiple times, exactly which - occurrence gets returned is undefined. Use get_all() to get all - the values matching a header field name. - """ - return self.get(name) - - def __setitem__(self, name, val): - """Set the value of a header. - - Note: this does not overwrite an existing header with the same field - name. Use __delitem__() first to delete any existing headers. - """ - max_count = self.policy.header_max_count(name) - if max_count: - lname = name.lower() - found = 0 - for k, v in self._headers: - if k.lower() == lname: - found += 1 - if found >= max_count: - raise ValueError("There may be at most {} {} headers " - "in a message".format(max_count, name)) - self._headers.append(self.policy.header_store_parse(name, val)) - - def __delitem__(self, name): - """Delete all occurrences of a header, if present. - - Does not raise an exception if the header is missing. - """ - name = name.lower() - newheaders = list() - for k, v in self._headers: - if k.lower() != name: - newheaders.append((k, v)) - self._headers = newheaders - - def __contains__(self, name): - return name.lower() in [k.lower() for k, v in self._headers] - - def __iter__(self): - for field, value in self._headers: - yield field - - def keys(self): - """Return a list of all the message's header field names. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [k for k, v in self._headers] - - def values(self): - """Return a list of all the message's header values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [self.policy.header_fetch_parse(k, v) - for k, v in self._headers] - - def items(self): - """Get all the message's header fields and values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [(k, self.policy.header_fetch_parse(k, v)) - for k, v in self._headers] - - def get(self, name, failobj=None): - """Get a header value. - - Like __getitem__() but return failobj instead of None when the field - is missing. - """ - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - return self.policy.header_fetch_parse(k, v) - return failobj - - # - # "Internal" methods (public API, but only intended for use by a parser - # or generator, not normal application code. - # - - def set_raw(self, name, value): - """Store name and value in the model without modification. - - This is an "internal" API, intended only for use by a parser. - """ - self._headers.append((name, value)) - - def raw_items(self): - """Return the (name, value) header pairs without modification. - - This is an "internal" API, intended only for use by a generator. - """ - return iter(self._headers.copy()) - - # - # Additional useful stuff - # - - def get_all(self, name, failobj=None): - """Return a list of all the values for the named field. - - These will be sorted in the order they appeared in the original - message, and may contain duplicates. Any fields deleted and - re-inserted are always appended to the header list. - - If no such fields exist, failobj is returned (defaults to None). - """ - values = [] - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - values.append(self.policy.header_fetch_parse(k, v)) - if not values: - return failobj - return values - - def add_header(self, _name, _value, **_params): - """Extended header setting. - - name is the header field to add. keyword arguments can be used to set - additional parameters for the header field, with underscores converted - to dashes. Normally the parameter will be added as key="value" unless - value is None, in which case only the key will be added. If a - parameter value contains non-ASCII characters it can be specified as a - three-tuple of (charset, language, value), in which case it will be - encoded according to RFC2231 rules. Otherwise it will be encoded using - the utf-8 charset and a language of ''. - - Examples: - - msg.add_header('content-disposition', 'attachment', filename='bud.gif') - msg.add_header('content-disposition', 'attachment', - filename=('utf-8', '', 'Fußballer.ppt')) - msg.add_header('content-disposition', 'attachment', - filename='Fußballer.ppt')) - """ - parts = [] - for k, v in _params.items(): - if v is None: - parts.append(k.replace('_', '-')) - else: - parts.append(_formatparam(k.replace('_', '-'), v)) - if _value is not None: - parts.insert(0, _value) - self[_name] = SEMISPACE.join(parts) - - def replace_header(self, _name, _value): - """Replace a header. - - Replace the first matching header found in the message, retaining - header order and case. If no matching header was found, a KeyError is - raised. - """ - _name = _name.lower() - for i, (k, v) in zip(range(len(self._headers)), self._headers): - if k.lower() == _name: - self._headers[i] = self.policy.header_store_parse(k, _value) - break - else: - raise KeyError(_name) - - # - # Use these three methods instead of the three above. - # - - def get_content_type(self): - """Return the message's content type. - - The returned string is coerced to lower case of the form - `maintype/subtype'. If there was no Content-Type header in the - message, the default type as given by get_default_type() will be - returned. Since according to RFC 2045, messages always have a default - type this will always return a value. - - RFC 2045 defines a message's default type to be text/plain unless it - appears inside a multipart/digest container, in which case it would be - message/rfc822. - """ - missing = object() - value = self.get('content-type', missing) - if value is missing: - # This should have no parameters - return self.get_default_type() - ctype = _splitparam(value)[0].lower() - # RFC 2045, section 5.2 says if its invalid, use text/plain - if ctype.count('/') != 1: - return 'text/plain' - return ctype - - def get_content_maintype(self): - """Return the message's main content type. - - This is the `maintype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[0] - - def get_content_subtype(self): - """Returns the message's sub-content type. - - This is the `subtype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[1] - - def get_default_type(self): - """Return the `default' content type. - - Most messages have a default content type of text/plain, except for - messages that are subparts of multipart/digest containers. Such - subparts have a default content type of message/rfc822. - """ - return self._default_type - - def set_default_type(self, ctype): - """Set the `default' content type. - - ctype should be either "text/plain" or "message/rfc822", although this - is not enforced. The default content type is not stored in the - Content-Type header. - """ - self._default_type = ctype - - def _get_params_preserve(self, failobj, header): - # Like get_params() but preserves the quoting of values. BAW: - # should this be part of the public interface? - missing = object() - value = self.get(header, missing) - if value is missing: - return failobj - params = [] - for p in _parseparam(value): - try: - name, val = p.split('=', 1) - name = name.strip() - val = val.strip() - except ValueError: - # Must have been a bare attribute - name = p.strip() - val = '' - params.append((name, val)) - params = utils.decode_params(params) - return params - - def get_params(self, failobj=None, header='content-type', unquote=True): - """Return the message's Content-Type parameters, as a list. - - The elements of the returned list are 2-tuples of key/value pairs, as - split on the `=' sign. The left hand side of the `=' is the key, - while the right hand side is the value. If there is no `=' sign in - the parameter the value is the empty string. The value is as - described in the get_param() method. - - Optional failobj is the object to return if there is no Content-Type - header. Optional header is the header to search instead of - Content-Type. If unquote is True, the value is unquoted. - """ - missing = object() - params = self._get_params_preserve(missing, header) - if params is missing: - return failobj - if unquote: - return [(k, _unquotevalue(v)) for k, v in params] - else: - return params - - def get_param(self, param, failobj=None, header='content-type', - unquote=True): - """Return the parameter value if found in the Content-Type header. - - Optional failobj is the object to return if there is no Content-Type - header, or the Content-Type header has no such parameter. Optional - header is the header to search instead of Content-Type. - - Parameter keys are always compared case insensitively. The return - value can either be a string, or a 3-tuple if the parameter was RFC - 2231 encoded. When it's a 3-tuple, the elements of the value are of - the form (CHARSET, LANGUAGE, VALUE). Note that both CHARSET and - LANGUAGE can be None, in which case you should consider VALUE to be - encoded in the us-ascii charset. You can usually ignore LANGUAGE. - The parameter value (either the returned string, or the VALUE item in - the 3-tuple) is always unquoted, unless unquote is set to False. - - If your application doesn't care whether the parameter was RFC 2231 - encoded, it can turn the return value into a string as follows: - - param = msg.get_param('foo') - param = email.utils.collapse_rfc2231_value(rawparam) - - """ - if header not in self: - return failobj - for k, v in self._get_params_preserve(failobj, header): - if k.lower() == param.lower(): - if unquote: - return _unquotevalue(v) - else: - return v - return failobj - - def set_param(self, param, value, header='Content-Type', requote=True, - charset=None, language=''): - """Set a parameter in the Content-Type header. - - If the parameter already exists in the header, its value will be - replaced with the new value. - - If header is Content-Type and has not yet been defined for this - message, it will be set to "text/plain" and the new parameter and - value will be appended as per RFC 2045. - - An alternate header can specified in the header argument, and all - parameters will be quoted as necessary unless requote is False. - - If charset is specified, the parameter will be encoded according to RFC - 2231. Optional language specifies the RFC 2231 language, defaulting - to the empty string. Both charset and language should be strings. - """ - if not isinstance(value, tuple) and charset: - value = (charset, language, value) - - if header not in self and header.lower() == 'content-type': - ctype = 'text/plain' - else: - ctype = self.get(header) - if not self.get_param(param, header=header): - if not ctype: - ctype = _formatparam(param, value, requote) - else: - ctype = SEMISPACE.join( - [ctype, _formatparam(param, value, requote)]) - else: - ctype = '' - for old_param, old_value in self.get_params(header=header, - unquote=requote): - append_param = '' - if old_param.lower() == param.lower(): - append_param = _formatparam(param, value, requote) - else: - append_param = _formatparam(old_param, old_value, requote) - if not ctype: - ctype = append_param - else: - ctype = SEMISPACE.join([ctype, append_param]) - if ctype != self.get(header): - del self[header] - self[header] = ctype - - def del_param(self, param, header='content-type', requote=True): - """Remove the given parameter completely from the Content-Type header. - - The header will be re-written in place without the parameter or its - value. All values will be quoted as necessary unless requote is - False. Optional header specifies an alternative to the Content-Type - header. - """ - if header not in self: - return - new_ctype = '' - for p, v in self.get_params(header=header, unquote=requote): - if p.lower() != param.lower(): - if not new_ctype: - new_ctype = _formatparam(p, v, requote) - else: - new_ctype = SEMISPACE.join([new_ctype, - _formatparam(p, v, requote)]) - if new_ctype != self.get(header): - del self[header] - self[header] = new_ctype - - def set_type(self, type, header='Content-Type', requote=True): - """Set the main type and subtype for the Content-Type header. - - type must be a string in the form "maintype/subtype", otherwise a - ValueError is raised. - - This method replaces the Content-Type header, keeping all the - parameters in place. If requote is False, this leaves the existing - header's quoting as is. Otherwise, the parameters will be quoted (the - default). - - An alternative header can be specified in the header argument. When - the Content-Type header is set, we'll always also add a MIME-Version - header. - """ - # BAW: should we be strict? - if not type.count('/') == 1: - raise ValueError - # Set the Content-Type, you get a MIME-Version - if header.lower() == 'content-type': - del self['mime-version'] - self['MIME-Version'] = '1.0' - if header not in self: - self[header] = type - return - params = self.get_params(header=header, unquote=requote) - del self[header] - self[header] = type - # Skip the first param; it's the old type. - for p, v in params[1:]: - self.set_param(p, v, header, requote) - - def get_filename(self, failobj=None): - """Return the filename associated with the payload if present. - - The filename is extracted from the Content-Disposition header's - `filename' parameter, and it is unquoted. If that header is missing - the `filename' parameter, this method falls back to looking for the - `name' parameter. - """ - missing = object() - filename = self.get_param('filename', missing, 'content-disposition') - if filename is missing: - filename = self.get_param('name', missing, 'content-type') - if filename is missing: - return failobj - return utils.collapse_rfc2231_value(filename).strip() - - def get_boundary(self, failobj=None): - """Return the boundary associated with the payload if present. - - The boundary is extracted from the Content-Type header's `boundary' - parameter, and it is unquoted. - """ - missing = object() - boundary = self.get_param('boundary', missing) - if boundary is missing: - return failobj - # RFC 2046 says that boundaries may begin but not end in w/s - return utils.collapse_rfc2231_value(boundary).rstrip() - - def set_boundary(self, boundary): - """Set the boundary parameter in Content-Type to 'boundary'. - - This is subtly different than deleting the Content-Type header and - adding a new one with a new boundary parameter via add_header(). The - main difference is that using the set_boundary() method preserves the - order of the Content-Type header in the original message. - - HeaderParseError is raised if the message has no Content-Type header. - """ - missing = object() - params = self._get_params_preserve(missing, 'content-type') - if params is missing: - # There was no Content-Type header, and we don't know what type - # to set it to, so raise an exception. - raise errors.HeaderParseError('No Content-Type header found') - newparams = [] - foundp = False - for pk, pv in params: - if pk.lower() == 'boundary': - newparams.append(('boundary', '"%s"' % boundary)) - foundp = True - else: - newparams.append((pk, pv)) - if not foundp: - # The original Content-Type header had no boundary attribute. - # Tack one on the end. BAW: should we raise an exception - # instead??? - newparams.append(('boundary', '"%s"' % boundary)) - # Replace the existing Content-Type header with the new value - newheaders = [] - for h, v in self._headers: - if h.lower() == 'content-type': - parts = [] - for k, v in newparams: - if v == '': - parts.append(k) - else: - parts.append('%s=%s' % (k, v)) - val = SEMISPACE.join(parts) - newheaders.append(self.policy.header_store_parse(h, val)) - - else: - newheaders.append((h, v)) - self._headers = newheaders - - def get_content_charset(self, failobj=None): - """Return the charset parameter of the Content-Type header. - - The returned string is always coerced to lower case. If there is no - Content-Type header, or if that header has no charset parameter, - failobj is returned. - """ - missing = object() - charset = self.get_param('charset', missing) - if charset is missing: - return failobj - if isinstance(charset, tuple): - # RFC 2231 encoded, so decode it, and it better end up as ascii. - pcharset = charset[0] or 'us-ascii' - try: - # LookupError will be raised if the charset isn't known to - # Python. UnicodeError will be raised if the encoded text - # contains a character not in the charset. - as_bytes = charset[2].encode('raw-unicode-escape') - charset = str(as_bytes, pcharset) - except (LookupError, UnicodeError): - charset = charset[2] - # charset characters must be in us-ascii range - try: - charset.encode('us-ascii') - except UnicodeError: - return failobj - # RFC 2046, $4.1.2 says charsets are not case sensitive - return charset.lower() - - def get_charsets(self, failobj=None): - """Return a list containing the charset(s) used in this message. - - The returned list of items describes the Content-Type headers' - charset parameter for this message and all the subparts in its - payload. - - Each item will either be a string (the value of the charset parameter - in the Content-Type header of that part) or the value of the - 'failobj' parameter (defaults to None), if the part does not have a - main MIME type of "text", or the charset is not defined. - - The list will contain one string for each part of the message, plus - one for the container message (i.e. self), so that a non-multipart - message will still return a list of length 1. - """ - return [part.get_content_charset(failobj) for part in self.walk()] - - # I.e. def walk(self): ... - from future.backports.email.iterators import walk diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/application.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/application.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/application.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/application.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Keith Dart -# Contact: email-sig@python.org - -"""Class representing application/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - -__all__ = ["MIMEApplication"] - - -class MIMEApplication(MIMENonMultipart): - """Class for generating application/* MIME documents.""" - - def __init__(self, _data, _subtype='octet-stream', - _encoder=encoders.encode_base64, **_params): - """Create an application/* type MIME document. - - _data is a string containing the raw application data. - - _subtype is the MIME content type subtype, defaulting to - 'octet-stream'. - - _encoder is a function which will perform the actual encoding for - transport of the application data, defaulting to base64 encoding. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - raise TypeError('Invalid application MIME subtype') - MIMENonMultipart.__init__(self, 'application', _subtype, **_params) - self.set_payload(_data) - _encoder(self) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/audio.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/audio.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/audio.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/audio.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Anthony Baxter -# Contact: email-sig@python.org - -"""Class representing audio/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEAudio'] - -import sndhdr - -from io import BytesIO -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -_sndhdr_MIMEmap = {'au' : 'basic', - 'wav' :'x-wav', - 'aiff':'x-aiff', - 'aifc':'x-aiff', - } - -# There are others in sndhdr that don't have MIME types. :( -# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? -def _whatsnd(data): - """Try to identify a sound file type. - - sndhdr.what() has a pretty cruddy interface, unfortunately. This is why - we re-do it here. It would be easier to reverse engineer the Unix 'file' - command and use the standard 'magic' file, as shipped with a modern Unix. - """ - hdr = data[:512] - fakefile = BytesIO(hdr) - for testfn in sndhdr.tests: - res = testfn(hdr, fakefile) - if res is not None: - return _sndhdr_MIMEmap.get(res[0]) - return None - - -class MIMEAudio(MIMENonMultipart): - """Class for generating audio/* MIME documents.""" - - def __init__(self, _audiodata, _subtype=None, - _encoder=encoders.encode_base64, **_params): - """Create an audio/* type MIME document. - - _audiodata is a string containing the raw audio data. If this data - can be decoded by the standard Python `sndhdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific audio subtype via the - _subtype parameter. If _subtype is not given, and no subtype can be - guessed, a TypeError is raised. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = _whatsnd(_audiodata) - if _subtype is None: - raise TypeError('Could not find audio MIME subtype') - MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) - self.set_payload(_audiodata) - _encoder(self) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/base.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/base.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME specializations.""" -from __future__ import absolute_import, division, unicode_literals -from future.backports.email import message - -__all__ = ['MIMEBase'] - - -class MIMEBase(message.Message): - """Base class for MIME specializations.""" - - def __init__(self, _maintype, _subtype, **_params): - """This constructor adds a Content-Type: and a MIME-Version: header. - - The Content-Type: header is taken from the _maintype and _subtype - arguments. Additional parameters for this header are taken from the - keyword arguments. - """ - message.Message.__init__(self) - ctype = '%s/%s' % (_maintype, _subtype) - self.add_header('Content-Type', ctype, **_params) - self['MIME-Version'] = '1.0' diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/image.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/image.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/image.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/image.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing image/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEImage'] - -import imghdr - -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEImage(MIMENonMultipart): - """Class for generating image/* type MIME documents.""" - - def __init__(self, _imagedata, _subtype=None, - _encoder=encoders.encode_base64, **_params): - """Create an image/* type MIME document. - - _imagedata is a string containing the raw image data. If this data - can be decoded by the standard Python `imghdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific image subtype via the _subtype - parameter. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = imghdr.what(None, _imagedata) - if _subtype is None: - raise TypeError('Could not guess image MIME subtype') - MIMENonMultipart.__init__(self, 'image', _subtype, **_params) - self.set_payload(_imagedata) - _encoder(self) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/message.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/message.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/message.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/message.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing message/* MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEMessage'] - -from future.backports.email import message -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEMessage(MIMENonMultipart): - """Class representing message/* MIME documents.""" - - def __init__(self, _msg, _subtype='rfc822'): - """Create a message/* type MIME document. - - _msg is a message object and must be an instance of Message, or a - derived class of Message, otherwise a TypeError is raised. - - Optional _subtype defines the subtype of the contained message. The - default is "rfc822" (this is defined by the MIME standard, even though - the term "rfc822" is technically outdated by RFC 2822). - """ - MIMENonMultipart.__init__(self, 'message', _subtype) - if not isinstance(_msg, message.Message): - raise TypeError('Argument is not an instance of Message') - # It's convenient to use this base class method. We need to do it - # this way or we'll get an exception - message.Message.attach(self, _msg) - # And be sure our default type is set correctly - self.set_default_type('message/rfc822') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/multipart.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/multipart.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/multipart.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/multipart.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# Copyright (C) 2002-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME multipart/* type messages.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEMultipart'] - -from future.backports.email.mime.base import MIMEBase - - -class MIMEMultipart(MIMEBase): - """Base class for MIME multipart/* type messages.""" - - def __init__(self, _subtype='mixed', boundary=None, _subparts=None, - **_params): - """Creates a multipart/* type message. - - By default, creates a multipart/mixed message, with proper - Content-Type and MIME-Version headers. - - _subtype is the subtype of the multipart content type, defaulting to - `mixed'. - - boundary is the multipart boundary string. By default it is - calculated as needed. - - _subparts is a sequence of initial subparts for the payload. It - must be an iterable object, such as a list. You can always - attach new subparts to the message by using the attach() method. - - Additional parameters for the Content-Type header are taken from the - keyword arguments (or passed into the _params argument). - """ - MIMEBase.__init__(self, 'multipart', _subtype, **_params) - - # Initialise _payload to an empty list as the Message superclass's - # implementation of is_multipart assumes that _payload is a list for - # multipart messages. - self._payload = [] - - if _subparts: - for p in _subparts: - self.attach(p) - if boundary: - self.set_boundary(boundary) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -# Copyright (C) 2002-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME type messages that are not multipart.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMENonMultipart'] - -from future.backports.email import errors -from future.backports.email.mime.base import MIMEBase - - -class MIMENonMultipart(MIMEBase): - """Base class for MIME multipart/* type messages.""" - - def attach(self, payload): - # The public API prohibits attaching multiple subparts to MIMEBase - # derived subtypes since none of them are, by definition, of content - # type multipart/* - raise errors.MultipartConversionError( - 'Cannot attach additional subparts to non-multipart/*') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/text.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/text.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/mime/text.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/mime/text.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing text/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEText'] - -from future.backports.email.encoders import encode_7or8bit -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEText(MIMENonMultipart): - """Class for generating text/* type MIME documents.""" - - def __init__(self, _text, _subtype='plain', _charset=None): - """Create a text/* type MIME document. - - _text is the string for this message object. - - _subtype is the MIME sub content type, defaulting to "plain". - - _charset is the character set parameter added to the Content-Type - header. This defaults to "us-ascii". Note that as a side-effect, the - Content-Transfer-Encoding header will also be set. - """ - - # If no _charset was specified, check to see if there are non-ascii - # characters present. If not, use 'us-ascii', otherwise use utf-8. - # XXX: This can be removed once #7304 is fixed. - if _charset is None: - try: - _text.encode('us-ascii') - _charset = 'us-ascii' - except UnicodeEncodeError: - _charset = 'utf-8' - - MIMENonMultipart.__init__(self, 'text', _subtype, - **{'charset': _charset}) - - self.set_payload(_text, _charset) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_parseaddr.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_parseaddr.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_parseaddr.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_parseaddr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,546 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Contact: email-sig@python.org - -"""Email address parsing code. - -Lifted directly from rfc822.py. This should eventually be rewritten. -""" - -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import int - -__all__ = [ - 'mktime_tz', - 'parsedate', - 'parsedate_tz', - 'quote', - ] - -import time, calendar - -SPACE = ' ' -EMPTYSTRING = '' -COMMASPACE = ', ' - -# Parse a date field -_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', - 'aug', 'sep', 'oct', 'nov', 'dec', - 'january', 'february', 'march', 'april', 'may', 'june', 'july', - 'august', 'september', 'october', 'november', 'december'] - -_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] - -# The timezone table does not include the military time zones defined -# in RFC822, other than Z. According to RFC1123, the description in -# RFC822 gets the signs wrong, so we can't rely on any such time -# zones. RFC1123 recommends that numeric timezone indicators be used -# instead of timezone names. - -_timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0, - 'AST': -400, 'ADT': -300, # Atlantic (used in Canada) - 'EST': -500, 'EDT': -400, # Eastern - 'CST': -600, 'CDT': -500, # Central - 'MST': -700, 'MDT': -600, # Mountain - 'PST': -800, 'PDT': -700 # Pacific - } - - -def parsedate_tz(data): - """Convert a date string to a time tuple. - - Accounts for military timezones. - """ - res = _parsedate_tz(data) - if not res: - return - if res[9] is None: - res[9] = 0 - return tuple(res) - -def _parsedate_tz(data): - """Convert date to extended time tuple. - - The last (additional) element is the time zone offset in seconds, except if - the timezone was specified as -0000. In that case the last element is - None. This indicates a UTC timestamp that explicitly declaims knowledge of - the source timezone, as opposed to a +0000 timestamp that indicates the - source timezone really was UTC. - - """ - if not data: - return - data = data.split() - # The FWS after the comma after the day-of-week is optional, so search and - # adjust for this. - if data[0].endswith(',') or data[0].lower() in _daynames: - # There's a dayname here. Skip it - del data[0] - else: - i = data[0].rfind(',') - if i >= 0: - data[0] = data[0][i+1:] - if len(data) == 3: # RFC 850 date, deprecated - stuff = data[0].split('-') - if len(stuff) == 3: - data = stuff + data[1:] - if len(data) == 4: - s = data[3] - i = s.find('+') - if i == -1: - i = s.find('-') - if i > 0: - data[3:] = [s[:i], s[i:]] - else: - data.append('') # Dummy tz - if len(data) < 5: - return None - data = data[:5] - [dd, mm, yy, tm, tz] = data - mm = mm.lower() - if mm not in _monthnames: - dd, mm = mm, dd.lower() - if mm not in _monthnames: - return None - mm = _monthnames.index(mm) + 1 - if mm > 12: - mm -= 12 - if dd[-1] == ',': - dd = dd[:-1] - i = yy.find(':') - if i > 0: - yy, tm = tm, yy - if yy[-1] == ',': - yy = yy[:-1] - if not yy[0].isdigit(): - yy, tz = tz, yy - if tm[-1] == ',': - tm = tm[:-1] - tm = tm.split(':') - if len(tm) == 2: - [thh, tmm] = tm - tss = '0' - elif len(tm) == 3: - [thh, tmm, tss] = tm - elif len(tm) == 1 and '.' in tm[0]: - # Some non-compliant MUAs use '.' to separate time elements. - tm = tm[0].split('.') - if len(tm) == 2: - [thh, tmm] = tm - tss = 0 - elif len(tm) == 3: - [thh, tmm, tss] = tm - else: - return None - try: - yy = int(yy) - dd = int(dd) - thh = int(thh) - tmm = int(tmm) - tss = int(tss) - except ValueError: - return None - # Check for a yy specified in two-digit format, then convert it to the - # appropriate four-digit format, according to the POSIX standard. RFC 822 - # calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822) - # mandates a 4-digit yy. For more information, see the documentation for - # the time module. - if yy < 100: - # The year is between 1969 and 1999 (inclusive). - if yy > 68: - yy += 1900 - # The year is between 2000 and 2068 (inclusive). - else: - yy += 2000 - tzoffset = None - tz = tz.upper() - if tz in _timezones: - tzoffset = _timezones[tz] - else: - try: - tzoffset = int(tz) - except ValueError: - pass - if tzoffset==0 and tz.startswith('-'): - tzoffset = None - # Convert a timezone offset into seconds ; -0500 -> -18000 - if tzoffset: - if tzoffset < 0: - tzsign = -1 - tzoffset = -tzoffset - else: - tzsign = 1 - tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60) - # Daylight Saving Time flag is set to -1, since DST is unknown. - return [yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset] - - -def parsedate(data): - """Convert a time string to a time tuple.""" - t = parsedate_tz(data) - if isinstance(t, tuple): - return t[:9] - else: - return t - - -def mktime_tz(data): - """Turn a 10-tuple as returned by parsedate_tz() into a POSIX timestamp.""" - if data[9] is None: - # No zone info, so localtime is better assumption than GMT - return time.mktime(data[:8] + (-1,)) - else: - t = calendar.timegm(data) - return t - data[9] - - -def quote(str): - """Prepare string to be used in a quoted string. - - Turns backslash and double quote characters into quoted pairs. These - are the only characters that need to be quoted inside a quoted string. - Does not add the surrounding double quotes. - """ - return str.replace('\\', '\\\\').replace('"', '\\"') - - -class AddrlistClass(object): - """Address parser class by Ben Escoto. - - To understand what this class does, it helps to have a copy of RFC 2822 in - front of you. - - Note: this class interface is deprecated and may be removed in the future. - Use email.utils.AddressList instead. - """ - - def __init__(self, field): - """Initialize a new instance. - - `field' is an unparsed address header field, containing - one or more addresses. - """ - self.specials = '()<>@,:;.\"[]' - self.pos = 0 - self.LWS = ' \t' - self.CR = '\r\n' - self.FWS = self.LWS + self.CR - self.atomends = self.specials + self.LWS + self.CR - # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it - # is obsolete syntax. RFC 2822 requires that we recognize obsolete - # syntax, so allow dots in phrases. - self.phraseends = self.atomends.replace('.', '') - self.field = field - self.commentlist = [] - - def gotonext(self): - """Skip white space and extract comments.""" - wslist = [] - while self.pos < len(self.field): - if self.field[self.pos] in self.LWS + '\n\r': - if self.field[self.pos] not in '\n\r': - wslist.append(self.field[self.pos]) - self.pos += 1 - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - else: - break - return EMPTYSTRING.join(wslist) - - def getaddrlist(self): - """Parse all addresses. - - Returns a list containing all of the addresses. - """ - result = [] - while self.pos < len(self.field): - ad = self.getaddress() - if ad: - result += ad - else: - result.append(('', '')) - return result - - def getaddress(self): - """Parse the next address.""" - self.commentlist = [] - self.gotonext() - - oldpos = self.pos - oldcl = self.commentlist - plist = self.getphraselist() - - self.gotonext() - returnlist = [] - - if self.pos >= len(self.field): - # Bad email address technically, no domain. - if plist: - returnlist = [(SPACE.join(self.commentlist), plist[0])] - - elif self.field[self.pos] in '.@': - # email address is just an addrspec - # this isn't very efficient since we start over - self.pos = oldpos - self.commentlist = oldcl - addrspec = self.getaddrspec() - returnlist = [(SPACE.join(self.commentlist), addrspec)] - - elif self.field[self.pos] == ':': - # address is a group - returnlist = [] - - fieldlen = len(self.field) - self.pos += 1 - while self.pos < len(self.field): - self.gotonext() - if self.pos < fieldlen and self.field[self.pos] == ';': - self.pos += 1 - break - returnlist = returnlist + self.getaddress() - - elif self.field[self.pos] == '<': - # Address is a phrase then a route addr - routeaddr = self.getrouteaddr() - - if self.commentlist: - returnlist = [(SPACE.join(plist) + ' (' + - ' '.join(self.commentlist) + ')', routeaddr)] - else: - returnlist = [(SPACE.join(plist), routeaddr)] - - else: - if plist: - returnlist = [(SPACE.join(self.commentlist), plist[0])] - elif self.field[self.pos] in self.specials: - self.pos += 1 - - self.gotonext() - if self.pos < len(self.field) and self.field[self.pos] == ',': - self.pos += 1 - return returnlist - - def getrouteaddr(self): - """Parse a route address (Return-path value). - - This method just skips all the route stuff and returns the addrspec. - """ - if self.field[self.pos] != '<': - return - - expectroute = False - self.pos += 1 - self.gotonext() - adlist = '' - while self.pos < len(self.field): - if expectroute: - self.getdomain() - expectroute = False - elif self.field[self.pos] == '>': - self.pos += 1 - break - elif self.field[self.pos] == '@': - self.pos += 1 - expectroute = True - elif self.field[self.pos] == ':': - self.pos += 1 - else: - adlist = self.getaddrspec() - self.pos += 1 - break - self.gotonext() - - return adlist - - def getaddrspec(self): - """Parse an RFC 2822 addr-spec.""" - aslist = [] - - self.gotonext() - while self.pos < len(self.field): - preserve_ws = True - if self.field[self.pos] == '.': - if aslist and not aslist[-1].strip(): - aslist.pop() - aslist.append('.') - self.pos += 1 - preserve_ws = False - elif self.field[self.pos] == '"': - aslist.append('"%s"' % quote(self.getquote())) - elif self.field[self.pos] in self.atomends: - if aslist and not aslist[-1].strip(): - aslist.pop() - break - else: - aslist.append(self.getatom()) - ws = self.gotonext() - if preserve_ws and ws: - aslist.append(ws) - - if self.pos >= len(self.field) or self.field[self.pos] != '@': - return EMPTYSTRING.join(aslist) - - aslist.append('@') - self.pos += 1 - self.gotonext() - return EMPTYSTRING.join(aslist) + self.getdomain() - - def getdomain(self): - """Get the complete domain name from an address.""" - sdlist = [] - while self.pos < len(self.field): - if self.field[self.pos] in self.LWS: - self.pos += 1 - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - elif self.field[self.pos] == '[': - sdlist.append(self.getdomainliteral()) - elif self.field[self.pos] == '.': - self.pos += 1 - sdlist.append('.') - elif self.field[self.pos] in self.atomends: - break - else: - sdlist.append(self.getatom()) - return EMPTYSTRING.join(sdlist) - - def getdelimited(self, beginchar, endchars, allowcomments=True): - """Parse a header fragment delimited by special characters. - - `beginchar' is the start character for the fragment. - If self is not looking at an instance of `beginchar' then - getdelimited returns the empty string. - - `endchars' is a sequence of allowable end-delimiting characters. - Parsing stops when one of these is encountered. - - If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed - within the parsed fragment. - """ - if self.field[self.pos] != beginchar: - return '' - - slist = [''] - quote = False - self.pos += 1 - while self.pos < len(self.field): - if quote: - slist.append(self.field[self.pos]) - quote = False - elif self.field[self.pos] in endchars: - self.pos += 1 - break - elif allowcomments and self.field[self.pos] == '(': - slist.append(self.getcomment()) - continue # have already advanced pos from getcomment - elif self.field[self.pos] == '\\': - quote = True - else: - slist.append(self.field[self.pos]) - self.pos += 1 - - return EMPTYSTRING.join(slist) - - def getquote(self): - """Get a quote-delimited fragment from self's field.""" - return self.getdelimited('"', '"\r', False) - - def getcomment(self): - """Get a parenthesis-delimited fragment from self's field.""" - return self.getdelimited('(', ')\r', True) - - def getdomainliteral(self): - """Parse an RFC 2822 domain-literal.""" - return '[%s]' % self.getdelimited('[', ']\r', False) - - def getatom(self, atomends=None): - """Parse an RFC 2822 atom. - - Optional atomends specifies a different set of end token delimiters - (the default is to use self.atomends). This is used e.g. in - getphraselist() since phrase endings must not include the `.' (which - is legal in phrases).""" - atomlist = [''] - if atomends is None: - atomends = self.atomends - - while self.pos < len(self.field): - if self.field[self.pos] in atomends: - break - else: - atomlist.append(self.field[self.pos]) - self.pos += 1 - - return EMPTYSTRING.join(atomlist) - - def getphraselist(self): - """Parse a sequence of RFC 2822 phrases. - - A phrase is a sequence of words, which are in turn either RFC 2822 - atoms or quoted-strings. Phrases are canonicalized by squeezing all - runs of continuous whitespace into one space. - """ - plist = [] - - while self.pos < len(self.field): - if self.field[self.pos] in self.FWS: - self.pos += 1 - elif self.field[self.pos] == '"': - plist.append(self.getquote()) - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - elif self.field[self.pos] in self.phraseends: - break - else: - plist.append(self.getatom(self.phraseends)) - - return plist - -class AddressList(AddrlistClass): - """An AddressList encapsulates a list of parsed RFC 2822 addresses.""" - def __init__(self, field): - AddrlistClass.__init__(self, field) - if field: - self.addresslist = self.getaddrlist() - else: - self.addresslist = [] - - def __len__(self): - return len(self.addresslist) - - def __add__(self, other): - # Set union - newaddr = AddressList(None) - newaddr.addresslist = self.addresslist[:] - for x in other.addresslist: - if not x in self.addresslist: - newaddr.addresslist.append(x) - return newaddr - - def __iadd__(self, other): - # Set union, in-place - for x in other.addresslist: - if not x in self.addresslist: - self.addresslist.append(x) - return self - - def __sub__(self, other): - # Set difference - newaddr = AddressList(None) - for x in self.addresslist: - if not x in other.addresslist: - newaddr.addresslist.append(x) - return newaddr - - def __isub__(self, other): - # Set difference, in-place - for x in other.addresslist: - if x in self.addresslist: - self.addresslist.remove(x) - return self - - def __getitem__(self, index): - # Make indexing, slices, and 'in' work - return self.addresslist[index] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/parser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/parser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw, Thomas Wouters, Anthony Baxter -# Contact: email-sig@python.org - -"""A parser of RFC 2822 and MIME email messages.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['Parser', 'HeaderParser', 'BytesParser', 'BytesHeaderParser'] - -import warnings -from io import StringIO, TextIOWrapper - -from future.backports.email.feedparser import FeedParser, BytesFeedParser -from future.backports.email.message import Message -from future.backports.email._policybase import compat32 - - -class Parser(object): - def __init__(self, _class=Message, **_3to2kwargs): - """Parser of RFC 2822 and MIME email messages. - - Creates an in-memory object tree representing the email message, which - can then be manipulated and turned over to a Generator to return the - textual representation of the message. - - The string must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The - header block is terminated either by the end of the string or by a - blank line. - - _class is the class to instantiate for new message objects when they - must be created. This class must have a constructor that can take - zero arguments. Default is Message.Message. - - The policy keyword specifies a policy object that controls a number of - aspects of the parser's operation. The default policy maintains - backward compatibility. - - """ - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = compat32 - self._class = _class - self.policy = policy - - def parse(self, fp, headersonly=False): - """Create a message structure from the data in a file. - - Reads all the data from the file and returns the root of the message - structure. Optional headersonly is a flag specifying whether to stop - parsing after reading the headers or not. The default is False, - meaning it parses the entire contents of the file. - """ - feedparser = FeedParser(self._class, policy=self.policy) - if headersonly: - feedparser._set_headersonly() - while True: - data = fp.read(8192) - if not data: - break - feedparser.feed(data) - return feedparser.close() - - def parsestr(self, text, headersonly=False): - """Create a message structure from a string. - - Returns the root of the message structure. Optional headersonly is a - flag specifying whether to stop parsing after reading the headers or - not. The default is False, meaning it parses the entire contents of - the file. - """ - return self.parse(StringIO(text), headersonly=headersonly) - - - -class HeaderParser(Parser): - def parse(self, fp, headersonly=True): - return Parser.parse(self, fp, True) - - def parsestr(self, text, headersonly=True): - return Parser.parsestr(self, text, True) - - -class BytesParser(object): - - def __init__(self, *args, **kw): - """Parser of binary RFC 2822 and MIME email messages. - - Creates an in-memory object tree representing the email message, which - can then be manipulated and turned over to a Generator to return the - textual representation of the message. - - The input must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The - header block is terminated either by the end of the input or by a - blank line. - - _class is the class to instantiate for new message objects when they - must be created. This class must have a constructor that can take - zero arguments. Default is Message.Message. - """ - self.parser = Parser(*args, **kw) - - def parse(self, fp, headersonly=False): - """Create a message structure from the data in a binary file. - - Reads all the data from the file and returns the root of the message - structure. Optional headersonly is a flag specifying whether to stop - parsing after reading the headers or not. The default is False, - meaning it parses the entire contents of the file. - """ - fp = TextIOWrapper(fp, encoding='ascii', errors='surrogateescape') - with fp: - return self.parser.parse(fp, headersonly) - - - def parsebytes(self, text, headersonly=False): - """Create a message structure from a byte string. - - Returns the root of the message structure. Optional headersonly is a - flag specifying whether to stop parsing after reading the headers or - not. The default is False, meaning it parses the entire contents of - the file. - """ - text = text.decode('ASCII', errors='surrogateescape') - return self.parser.parsestr(text, headersonly) - - -class BytesHeaderParser(BytesParser): - def parse(self, fp, headersonly=True): - return BytesParser.parse(self, fp, headersonly=True) - - def parsebytes(self, text, headersonly=True): - return BytesParser.parsebytes(self, text, headersonly=True) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_policybase.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_policybase.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/_policybase.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/_policybase.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,365 +0,0 @@ -"""Policy framework for the email package. - -Allows fine grained feature control of how the package parses and emits data. -""" -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import super -from future.builtins import str -from future.utils import with_metaclass - -import abc -from future.backports.email import header -from future.backports.email import charset as _charset -from future.backports.email.utils import _has_surrogates - -__all__ = [ - 'Policy', - 'Compat32', - 'compat32', - ] - - -class _PolicyBase(object): - - """Policy Object basic framework. - - This class is useless unless subclassed. A subclass should define - class attributes with defaults for any values that are to be - managed by the Policy object. The constructor will then allow - non-default values to be set for these attributes at instance - creation time. The instance will be callable, taking these same - attributes keyword arguments, and returning a new instance - identical to the called instance except for those values changed - by the keyword arguments. Instances may be added, yielding new - instances with any non-default values from the right hand - operand overriding those in the left hand operand. That is, - - A + B == A() - - The repr of an instance can be used to reconstruct the object - if and only if the repr of the values can be used to reconstruct - those values. - - """ - - def __init__(self, **kw): - """Create new Policy, possibly overriding some defaults. - - See class docstring for a list of overridable attributes. - - """ - for name, value in kw.items(): - if hasattr(self, name): - super(_PolicyBase,self).__setattr__(name, value) - else: - raise TypeError( - "{!r} is an invalid keyword argument for {}".format( - name, self.__class__.__name__)) - - def __repr__(self): - args = [ "{}={!r}".format(name, value) - for name, value in self.__dict__.items() ] - return "{}({})".format(self.__class__.__name__, ', '.join(args)) - - def clone(self, **kw): - """Return a new instance with specified attributes changed. - - The new instance has the same attribute values as the current object, - except for the changes passed in as keyword arguments. - - """ - newpolicy = self.__class__.__new__(self.__class__) - for attr, value in self.__dict__.items(): - object.__setattr__(newpolicy, attr, value) - for attr, value in kw.items(): - if not hasattr(self, attr): - raise TypeError( - "{!r} is an invalid keyword argument for {}".format( - attr, self.__class__.__name__)) - object.__setattr__(newpolicy, attr, value) - return newpolicy - - def __setattr__(self, name, value): - if hasattr(self, name): - msg = "{!r} object attribute {!r} is read-only" - else: - msg = "{!r} object has no attribute {!r}" - raise AttributeError(msg.format(self.__class__.__name__, name)) - - def __add__(self, other): - """Non-default values from right operand override those from left. - - The object returned is a new instance of the subclass. - - """ - return self.clone(**other.__dict__) - - -def _append_doc(doc, added_doc): - doc = doc.rsplit('\n', 1)[0] - added_doc = added_doc.split('\n', 1)[1] - return doc + '\n' + added_doc - -def _extend_docstrings(cls): - if cls.__doc__ and cls.__doc__.startswith('+'): - cls.__doc__ = _append_doc(cls.__bases__[0].__doc__, cls.__doc__) - for name, attr in cls.__dict__.items(): - if attr.__doc__ and attr.__doc__.startswith('+'): - for c in (c for base in cls.__bases__ for c in base.mro()): - doc = getattr(getattr(c, name), '__doc__') - if doc: - attr.__doc__ = _append_doc(doc, attr.__doc__) - break - return cls - - -class Policy(with_metaclass(abc.ABCMeta, _PolicyBase)): - - r"""Controls for how messages are interpreted and formatted. - - Most of the classes and many of the methods in the email package accept - Policy objects as parameters. A Policy object contains a set of values and - functions that control how input is interpreted and how output is rendered. - For example, the parameter 'raise_on_defect' controls whether or not an RFC - violation results in an error being raised or not, while 'max_line_length' - controls the maximum length of output lines when a Message is serialized. - - Any valid attribute may be overridden when a Policy is created by passing - it as a keyword argument to the constructor. Policy objects are immutable, - but a new Policy object can be created with only certain values changed by - calling the Policy instance with keyword arguments. Policy objects can - also be added, producing a new Policy object in which the non-default - attributes set in the right hand operand overwrite those specified in the - left operand. - - Settable attributes: - - raise_on_defect -- If true, then defects should be raised as errors. - Default: False. - - linesep -- string containing the value to use as separation - between output lines. Default '\n'. - - cte_type -- Type of allowed content transfer encodings - - 7bit -- ASCII only - 8bit -- Content-Transfer-Encoding: 8bit is allowed - - Default: 8bit. Also controls the disposition of - (RFC invalid) binary data in headers; see the - documentation of the binary_fold method. - - max_line_length -- maximum length of lines, excluding 'linesep', - during serialization. None or 0 means no line - wrapping is done. Default is 78. - - """ - - raise_on_defect = False - linesep = '\n' - cte_type = '8bit' - max_line_length = 78 - - def handle_defect(self, obj, defect): - """Based on policy, either raise defect or call register_defect. - - handle_defect(obj, defect) - - defect should be a Defect subclass, but in any case must be an - Exception subclass. obj is the object on which the defect should be - registered if it is not raised. If the raise_on_defect is True, the - defect is raised as an error, otherwise the object and the defect are - passed to register_defect. - - This method is intended to be called by parsers that discover defects. - The email package parsers always call it with Defect instances. - - """ - if self.raise_on_defect: - raise defect - self.register_defect(obj, defect) - - def register_defect(self, obj, defect): - """Record 'defect' on 'obj'. - - Called by handle_defect if raise_on_defect is False. This method is - part of the Policy API so that Policy subclasses can implement custom - defect handling. The default implementation calls the append method of - the defects attribute of obj. The objects used by the email package by - default that get passed to this method will always have a defects - attribute with an append method. - - """ - obj.defects.append(defect) - - def header_max_count(self, name): - """Return the maximum allowed number of headers named 'name'. - - Called when a header is added to a Message object. If the returned - value is not 0 or None, and there are already a number of headers with - the name 'name' equal to the value returned, a ValueError is raised. - - Because the default behavior of Message's __setitem__ is to append the - value to the list of headers, it is easy to create duplicate headers - without realizing it. This method allows certain headers to be limited - in the number of instances of that header that may be added to a - Message programmatically. (The limit is not observed by the parser, - which will faithfully produce as many headers as exist in the message - being parsed.) - - The default implementation returns None for all header names. - """ - return None - - @abc.abstractmethod - def header_source_parse(self, sourcelines): - """Given a list of linesep terminated strings constituting the lines of - a single header, return the (name, value) tuple that should be stored - in the model. The input lines should retain their terminating linesep - characters. The lines passed in by the email package may contain - surrogateescaped binary data. - """ - raise NotImplementedError - - @abc.abstractmethod - def header_store_parse(self, name, value): - """Given the header name and the value provided by the application - program, return the (name, value) that should be stored in the model. - """ - raise NotImplementedError - - @abc.abstractmethod - def header_fetch_parse(self, name, value): - """Given the header name and the value from the model, return the value - to be returned to the application program that is requesting that - header. The value passed in by the email package may contain - surrogateescaped binary data if the lines were parsed by a BytesParser. - The returned value should not contain any surrogateescaped data. - - """ - raise NotImplementedError - - @abc.abstractmethod - def fold(self, name, value): - """Given the header name and the value from the model, return a string - containing linesep characters that implement the folding of the header - according to the policy controls. The value passed in by the email - package may contain surrogateescaped binary data if the lines were - parsed by a BytesParser. The returned value should not contain any - surrogateescaped data. - - """ - raise NotImplementedError - - @abc.abstractmethod - def fold_binary(self, name, value): - """Given the header name and the value from the model, return binary - data containing linesep characters that implement the folding of the - header according to the policy controls. The value passed in by the - email package may contain surrogateescaped binary data. - - """ - raise NotImplementedError - - -@_extend_docstrings -class Compat32(Policy): - - """+ - This particular policy is the backward compatibility Policy. It - replicates the behavior of the email package version 5.1. - """ - - def _sanitize_header(self, name, value): - # If the header value contains surrogates, return a Header using - # the unknown-8bit charset to encode the bytes as encoded words. - if not isinstance(value, str): - # Assume it is already a header object - return value - if _has_surrogates(value): - return header.Header(value, charset=_charset.UNKNOWN8BIT, - header_name=name) - else: - return value - - def header_source_parse(self, sourcelines): - """+ - The name is parsed as everything up to the ':' and returned unmodified. - The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and - stripping any trailing carriage return or linefeed characters. - - """ - name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) - return (name, value.rstrip('\r\n')) - - def header_store_parse(self, name, value): - """+ - The name and value are returned unmodified. - """ - return (name, value) - - def header_fetch_parse(self, name, value): - """+ - If the value contains binary data, it is converted into a Header object - using the unknown-8bit charset. Otherwise it is returned unmodified. - """ - return self._sanitize_header(name, value) - - def fold(self, name, value): - """+ - Headers are folded using the Header folding algorithm, which preserves - existing line breaks in the value, and wraps each resulting line to the - max_line_length. Non-ASCII binary data are CTE encoded using the - unknown-8bit charset. - - """ - return self._fold(name, value, sanitize=True) - - def fold_binary(self, name, value): - """+ - Headers are folded using the Header folding algorithm, which preserves - existing line breaks in the value, and wraps each resulting line to the - max_line_length. If cte_type is 7bit, non-ascii binary data is CTE - encoded using the unknown-8bit charset. Otherwise the original source - header is used, with its existing line breaks and/or binary data. - - """ - folded = self._fold(name, value, sanitize=self.cte_type=='7bit') - return folded.encode('ascii', 'surrogateescape') - - def _fold(self, name, value, sanitize): - parts = [] - parts.append('%s: ' % name) - if isinstance(value, str): - if _has_surrogates(value): - if sanitize: - h = header.Header(value, - charset=_charset.UNKNOWN8BIT, - header_name=name) - else: - # If we have raw 8bit data in a byte string, we have no idea - # what the encoding is. There is no safe way to split this - # string. If it's ascii-subset, then we could do a normal - # ascii split, but if it's multibyte then we could break the - # string. There's no way to know so the least harm seems to - # be to not split the string and risk it being too long. - parts.append(value) - h = None - else: - h = header.Header(value, header_name=name) - else: - # Assume it is a Header-like object. - h = value - if h is not None: - parts.append(h.encode(linesep=self.linesep, - maxlinelen=self.max_line_length)) - parts.append(self.linesep) - return ''.join(parts) - - -compat32 = Compat32() diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/policy.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/policy.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/policy.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/policy.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,193 +0,0 @@ -"""This will be the home for the policy that hooks in the new -code that adds all the email6 features. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super - -from future.standard_library.email._policybase import (Policy, Compat32, - compat32, _extend_docstrings) -from future.standard_library.email.utils import _has_surrogates -from future.standard_library.email.headerregistry import HeaderRegistry as HeaderRegistry - -__all__ = [ - 'Compat32', - 'compat32', - 'Policy', - 'EmailPolicy', - 'default', - 'strict', - 'SMTP', - 'HTTP', - ] - -@_extend_docstrings -class EmailPolicy(Policy): - - """+ - PROVISIONAL - - The API extensions enabled by this policy are currently provisional. - Refer to the documentation for details. - - This policy adds new header parsing and folding algorithms. Instead of - simple strings, headers are custom objects with custom attributes - depending on the type of the field. The folding algorithm fully - implements RFCs 2047 and 5322. - - In addition to the settable attributes listed above that apply to - all Policies, this policy adds the following additional attributes: - - refold_source -- if the value for a header in the Message object - came from the parsing of some source, this attribute - indicates whether or not a generator should refold - that value when transforming the message back into - stream form. The possible values are: - - none -- all source values use original folding - long -- source values that have any line that is - longer than max_line_length will be - refolded - all -- all values are refolded. - - The default is 'long'. - - header_factory -- a callable that takes two arguments, 'name' and - 'value', where 'name' is a header field name and - 'value' is an unfolded header field value, and - returns a string-like object that represents that - header. A default header_factory is provided that - understands some of the RFC5322 header field types. - (Currently address fields and date fields have - special treatment, while all other fields are - treated as unstructured. This list will be - completed before the extension is marked stable.) - """ - - refold_source = 'long' - header_factory = HeaderRegistry() - - def __init__(self, **kw): - # Ensure that each new instance gets a unique header factory - # (as opposed to clones, which share the factory). - if 'header_factory' not in kw: - object.__setattr__(self, 'header_factory', HeaderRegistry()) - super().__init__(**kw) - - def header_max_count(self, name): - """+ - The implementation for this class returns the max_count attribute from - the specialized header class that would be used to construct a header - of type 'name'. - """ - return self.header_factory[name].max_count - - # The logic of the next three methods is chosen such that it is possible to - # switch a Message object between a Compat32 policy and a policy derived - # from this class and have the results stay consistent. This allows a - # Message object constructed with this policy to be passed to a library - # that only handles Compat32 objects, or to receive such an object and - # convert it to use the newer style by just changing its policy. It is - # also chosen because it postpones the relatively expensive full rfc5322 - # parse until as late as possible when parsing from source, since in many - # applications only a few headers will actually be inspected. - - def header_source_parse(self, sourcelines): - """+ - The name is parsed as everything up to the ':' and returned unmodified. - The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and - stripping any trailing carriage return or linefeed characters. (This - is the same as Compat32). - - """ - name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) - return (name, value.rstrip('\r\n')) - - def header_store_parse(self, name, value): - """+ - The name is returned unchanged. If the input value has a 'name' - attribute and it matches the name ignoring case, the value is returned - unchanged. Otherwise the name and value are passed to header_factory - method, and the resulting custom header object is returned as the - value. In this case a ValueError is raised if the input value contains - CR or LF characters. - - """ - if hasattr(value, 'name') and value.name.lower() == name.lower(): - return (name, value) - if isinstance(value, str) and len(value.splitlines())>1: - raise ValueError("Header values may not contain linefeed " - "or carriage return characters") - return (name, self.header_factory(name, value)) - - def header_fetch_parse(self, name, value): - """+ - If the value has a 'name' attribute, it is returned to unmodified. - Otherwise the name and the value with any linesep characters removed - are passed to the header_factory method, and the resulting custom - header object is returned. Any surrogateescaped bytes get turned - into the unicode unknown-character glyph. - - """ - if hasattr(value, 'name'): - return value - return self.header_factory(name, ''.join(value.splitlines())) - - def fold(self, name, value): - """+ - Header folding is controlled by the refold_source policy setting. A - value is considered to be a 'source value' if and only if it does not - have a 'name' attribute (having a 'name' attribute means it is a header - object of some sort). If a source value needs to be refolded according - to the policy, it is converted into a custom header object by passing - the name and the value with any linesep characters removed to the - header_factory method. Folding of a custom header object is done by - calling its fold method with the current policy. - - Source values are split into lines using splitlines. If the value is - not to be refolded, the lines are rejoined using the linesep from the - policy and returned. The exception is lines containing non-ascii - binary data. In that case the value is refolded regardless of the - refold_source setting, which causes the binary data to be CTE encoded - using the unknown-8bit charset. - - """ - return self._fold(name, value, refold_binary=True) - - def fold_binary(self, name, value): - """+ - The same as fold if cte_type is 7bit, except that the returned value is - bytes. - - If cte_type is 8bit, non-ASCII binary data is converted back into - bytes. Headers with binary data are not refolded, regardless of the - refold_header setting, since there is no way to know whether the binary - data consists of single byte characters or multibyte characters. - - """ - folded = self._fold(name, value, refold_binary=self.cte_type=='7bit') - return folded.encode('ascii', 'surrogateescape') - - def _fold(self, name, value, refold_binary=False): - if hasattr(value, 'name'): - return value.fold(policy=self) - maxlen = self.max_line_length if self.max_line_length else float('inf') - lines = value.splitlines() - refold = (self.refold_source == 'all' or - self.refold_source == 'long' and - (lines and len(lines[0])+len(name)+2 > maxlen or - any(len(x) > maxlen for x in lines[1:]))) - if refold or refold_binary and _has_surrogates(value): - return self.header_factory(name, ''.join(lines)).fold(policy=self) - return name + ': ' + self.linesep.join(lines) + self.linesep - - -default = EmailPolicy() -# Make the default policy use the class default header_factory -del default.header_factory -strict = default.clone(raise_on_defect=True) -SMTP = default.clone(linesep='\r\n') -HTTP = default.clone(linesep='\r\n', max_line_length=None) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/quoprimime.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/quoprimime.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/quoprimime.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/quoprimime.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,326 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Quoted-printable content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode US ASCII-like 8-bit data called `quoted-printable'. It is used to -safely encode text that is in a character set similar to the 7-bit US ASCII -character set, but that includes some 8-bit characters that are normally not -allowed in email bodies or headers. - -Quoted-printable is very space-inefficient for encoding binary files; use the -email.base64mime module for that instead. - -This module provides an interface to encode and decode both headers and bodies -with quoted-printable encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:/From:/Cc: etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character -conversion necessary for proper internationalized headers; it only -does dumb encoding and decoding. To deal with the various line -wrapping issues, use the email.header module. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes, chr, dict, int, range, super - -__all__ = [ - 'body_decode', - 'body_encode', - 'body_length', - 'decode', - 'decodestring', - 'header_decode', - 'header_encode', - 'header_length', - 'quote', - 'unquote', - ] - -import re -import io - -from string import ascii_letters, digits, hexdigits - -CRLF = '\r\n' -NL = '\n' -EMPTYSTRING = '' - -# Build a mapping of octets to the expansion of that octet. Since we're only -# going to have 256 of these things, this isn't terribly inefficient -# space-wise. Remember that headers and bodies have different sets of safe -# characters. Initialize both maps with the full expansion, and then override -# the safe bytes with the more compact form. -_QUOPRI_HEADER_MAP = dict((c, '=%02X' % c) for c in range(256)) -_QUOPRI_BODY_MAP = _QUOPRI_HEADER_MAP.copy() - -# Safe header bytes which need no encoding. -for c in bytes(b'-!*+/' + ascii_letters.encode('ascii') + digits.encode('ascii')): - _QUOPRI_HEADER_MAP[c] = chr(c) -# Headers have one other special encoding; spaces become underscores. -_QUOPRI_HEADER_MAP[ord(' ')] = '_' - -# Safe body bytes which need no encoding. -for c in bytes(b' !"#$%&\'()*+,-./0123456789:;<>' - b'?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`' - b'abcdefghijklmnopqrstuvwxyz{|}~\t'): - _QUOPRI_BODY_MAP[c] = chr(c) - - - -# Helpers -def header_check(octet): - """Return True if the octet should be escaped with header quopri.""" - return chr(octet) != _QUOPRI_HEADER_MAP[octet] - - -def body_check(octet): - """Return True if the octet should be escaped with body quopri.""" - return chr(octet) != _QUOPRI_BODY_MAP[octet] - - -def header_length(bytearray): - """Return a header quoted-printable encoding length. - - Note that this does not include any RFC 2047 chrome added by - `header_encode()`. - - :param bytearray: An array of bytes (a.k.a. octets). - :return: The length in bytes of the byte array when it is encoded with - quoted-printable for headers. - """ - return sum(len(_QUOPRI_HEADER_MAP[octet]) for octet in bytearray) - - -def body_length(bytearray): - """Return a body quoted-printable encoding length. - - :param bytearray: An array of bytes (a.k.a. octets). - :return: The length in bytes of the byte array when it is encoded with - quoted-printable for bodies. - """ - return sum(len(_QUOPRI_BODY_MAP[octet]) for octet in bytearray) - - -def _max_append(L, s, maxlen, extra=''): - if not isinstance(s, str): - s = chr(s) - if not L: - L.append(s.lstrip()) - elif len(L[-1]) + len(s) <= maxlen: - L[-1] += extra + s - else: - L.append(s.lstrip()) - - -def unquote(s): - """Turn a string in the form =AB to the ASCII character with value 0xab""" - return chr(int(s[1:3], 16)) - - -def quote(c): - return '=%02X' % ord(c) - - - -def header_encode(header_bytes, charset='iso-8859-1'): - """Encode a single header line with quoted-printable (like) encoding. - - Defined in RFC 2045, this `Q' encoding is similar to quoted-printable, but - used specifically for email header fields to allow charsets with mostly 7 - bit characters (and some 8 bit) to remain more or less readable in non-RFC - 2045 aware mail clients. - - charset names the character set to use in the RFC 2046 header. It - defaults to iso-8859-1. - """ - # Return empty headers as an empty string. - if not header_bytes: - return '' - # Iterate over every byte, encoding if necessary. - encoded = [] - for octet in header_bytes: - encoded.append(_QUOPRI_HEADER_MAP[octet]) - # Now add the RFC chrome to each encoded chunk and glue the chunks - # together. - return '=?%s?q?%s?=' % (charset, EMPTYSTRING.join(encoded)) - - -class _body_accumulator(io.StringIO): - - def __init__(self, maxlinelen, eol, *args, **kw): - super().__init__(*args, **kw) - self.eol = eol - self.maxlinelen = self.room = maxlinelen - - def write_str(self, s): - """Add string s to the accumulated body.""" - self.write(s) - self.room -= len(s) - - def newline(self): - """Write eol, then start new line.""" - self.write_str(self.eol) - self.room = self.maxlinelen - - def write_soft_break(self): - """Write a soft break, then start a new line.""" - self.write_str('=') - self.newline() - - def write_wrapped(self, s, extra_room=0): - """Add a soft line break if needed, then write s.""" - if self.room < len(s) + extra_room: - self.write_soft_break() - self.write_str(s) - - def write_char(self, c, is_last_char): - if not is_last_char: - # Another character follows on this line, so we must leave - # extra room, either for it or a soft break, and whitespace - # need not be quoted. - self.write_wrapped(c, extra_room=1) - elif c not in ' \t': - # For this and remaining cases, no more characters follow, - # so there is no need to reserve extra room (since a hard - # break will immediately follow). - self.write_wrapped(c) - elif self.room >= 3: - # It's a whitespace character at end-of-line, and we have room - # for the three-character quoted encoding. - self.write(quote(c)) - elif self.room == 2: - # There's room for the whitespace character and a soft break. - self.write(c) - self.write_soft_break() - else: - # There's room only for a soft break. The quoted whitespace - # will be the only content on the subsequent line. - self.write_soft_break() - self.write(quote(c)) - - -def body_encode(body, maxlinelen=76, eol=NL): - """Encode with quoted-printable, wrapping at maxlinelen characters. - - Each line of encoded text will end with eol, which defaults to "\\n". Set - this to "\\r\\n" if you will be using the result of this function directly - in an email. - - Each line will be wrapped at, at most, maxlinelen characters before the - eol string (maxlinelen defaults to 76 characters, the maximum value - permitted by RFC 2045). Long lines will have the 'soft line break' - quoted-printable character "=" appended to them, so the decoded text will - be identical to the original text. - - The minimum maxlinelen is 4 to have room for a quoted character ("=XX") - followed by a soft line break. Smaller values will generate a - ValueError. - - """ - - if maxlinelen < 4: - raise ValueError("maxlinelen must be at least 4") - if not body: - return body - - # The last line may or may not end in eol, but all other lines do. - last_has_eol = (body[-1] in '\r\n') - - # This accumulator will make it easier to build the encoded body. - encoded_body = _body_accumulator(maxlinelen, eol) - - lines = body.splitlines() - last_line_no = len(lines) - 1 - for line_no, line in enumerate(lines): - last_char_index = len(line) - 1 - for i, c in enumerate(line): - if body_check(ord(c)): - c = quote(c) - encoded_body.write_char(c, i==last_char_index) - # Add an eol if input line had eol. All input lines have eol except - # possibly the last one. - if line_no < last_line_no or last_has_eol: - encoded_body.newline() - - return encoded_body.getvalue() - - - -# BAW: I'm not sure if the intent was for the signature of this function to be -# the same as base64MIME.decode() or not... -def decode(encoded, eol=NL): - """Decode a quoted-printable string. - - Lines are separated with eol, which defaults to \\n. - """ - if not encoded: - return encoded - # BAW: see comment in encode() above. Again, we're building up the - # decoded string with string concatenation, which could be done much more - # efficiently. - decoded = '' - - for line in encoded.splitlines(): - line = line.rstrip() - if not line: - decoded += eol - continue - - i = 0 - n = len(line) - while i < n: - c = line[i] - if c != '=': - decoded += c - i += 1 - # Otherwise, c == "=". Are we at the end of the line? If so, add - # a soft line break. - elif i+1 == n: - i += 1 - continue - # Decode if in form =AB - elif i+2 < n and line[i+1] in hexdigits and line[i+2] in hexdigits: - decoded += unquote(line[i:i+3]) - i += 3 - # Otherwise, not in form =AB, pass literally - else: - decoded += c - i += 1 - - if i == n: - decoded += eol - # Special case if original string did not end with eol - if encoded[-1] not in '\r\n' and decoded.endswith(eol): - decoded = decoded[:-1] - return decoded - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode - - - -def _unquote_match(match): - """Turn a match in the form =AB to the ASCII character with value 0xab""" - s = match.group(0) - return unquote(s) - - -# Header decoding is done a bit differently -def header_decode(s): - """Decode a string encoded with RFC 2045 MIME header `Q' encoding. - - This function does not parse a full MIME header value encoded with - quoted-printable (like =?iso-8895-1?q?Hello_World?=) -- please use - the high level email.header class for that functionality. - """ - s = s.replace('_', ' ') - return re.sub(r'=[a-fA-F0-9]{2}', _unquote_match, s, re.ASCII) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/utils.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/utils.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/email/utils.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/email/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,400 +0,0 @@ -# Copyright (C) 2001-2010 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Miscellaneous utilities.""" - -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future import utils -from future.builtins import bytes, int, str - -__all__ = [ - 'collapse_rfc2231_value', - 'decode_params', - 'decode_rfc2231', - 'encode_rfc2231', - 'formataddr', - 'formatdate', - 'format_datetime', - 'getaddresses', - 'make_msgid', - 'mktime_tz', - 'parseaddr', - 'parsedate', - 'parsedate_tz', - 'parsedate_to_datetime', - 'unquote', - ] - -import os -import re -if utils.PY2: - re.ASCII = 0 -import time -import base64 -import random -import socket -from future.backports import datetime -from future.backports.urllib.parse import quote as url_quote, unquote as url_unquote -import warnings -from io import StringIO - -from future.backports.email._parseaddr import quote -from future.backports.email._parseaddr import AddressList as _AddressList -from future.backports.email._parseaddr import mktime_tz - -from future.backports.email._parseaddr import parsedate, parsedate_tz, _parsedate_tz - -from quopri import decodestring as _qdecode - -# Intrapackage imports -from future.backports.email.encoders import _bencode, _qencode -from future.backports.email.charset import Charset - -COMMASPACE = ', ' -EMPTYSTRING = '' -UEMPTYSTRING = '' -CRLF = '\r\n' -TICK = "'" - -specialsre = re.compile(r'[][\\()<>@,:;".]') -escapesre = re.compile(r'[\\"]') - -# How to figure out if we are processing strings that come from a byte -# source with undecodable characters. -_has_surrogates = re.compile( - '([^\ud800-\udbff]|\A)[\udc00-\udfff]([^\udc00-\udfff]|\Z)').search - -# How to deal with a string containing bytes before handing it to the -# application through the 'normal' interface. -def _sanitize(string): - # Turn any escaped bytes into unicode 'unknown' char. - original_bytes = string.encode('ascii', 'surrogateescape') - return original_bytes.decode('ascii', 'replace') - - -# Helpers - -def formataddr(pair, charset='utf-8'): - """The inverse of parseaddr(), this takes a 2-tuple of the form - (realname, email_address) and returns the string value suitable - for an RFC 2822 From, To or Cc header. - - If the first element of pair is false, then the second element is - returned unmodified. - - Optional charset if given is the character set that is used to encode - realname in case realname is not ASCII safe. Can be an instance of str or - a Charset-like object which has a header_encode method. Default is - 'utf-8'. - """ - name, address = pair - # The address MUST (per RFC) be ascii, so raise an UnicodeError if it isn't. - address.encode('ascii') - if name: - try: - name.encode('ascii') - except UnicodeEncodeError: - if isinstance(charset, str): - charset = Charset(charset) - encoded_name = charset.header_encode(name) - return "%s <%s>" % (encoded_name, address) - else: - quotes = '' - if specialsre.search(name): - quotes = '"' - name = escapesre.sub(r'\\\g<0>', name) - return '%s%s%s <%s>' % (quotes, name, quotes, address) - return address - - - -def getaddresses(fieldvalues): - """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" - all = COMMASPACE.join(fieldvalues) - a = _AddressList(all) - return a.addresslist - - - -ecre = re.compile(r''' - =\? # literal =? - (?P[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P.*?) # non-greedy up to the next ?= is the atom - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE) - - -def _format_timetuple_and_zone(timetuple, zone): - return '%s, %02d %s %04d %02d:%02d:%02d %s' % ( - ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][timetuple[6]], - timetuple[2], - ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][timetuple[1] - 1], - timetuple[0], timetuple[3], timetuple[4], timetuple[5], - zone) - -def formatdate(timeval=None, localtime=False, usegmt=False): - """Returns a date string as specified by RFC 2822, e.g.: - - Fri, 09 Nov 2001 01:08:47 -0000 - - Optional timeval if given is a floating point time value as accepted by - gmtime() and localtime(), otherwise the current time is used. - - Optional localtime is a flag that when True, interprets timeval, and - returns a date relative to the local timezone instead of UTC, properly - taking daylight savings time into account. - - Optional argument usegmt means that the timezone is written out as - an ascii string, not numeric one (so "GMT" instead of "+0000"). This - is needed for HTTP, and is only used when localtime==False. - """ - # Note: we cannot use strftime() because that honors the locale and RFC - # 2822 requires that day and month names be the English abbreviations. - if timeval is None: - timeval = time.time() - if localtime: - now = time.localtime(timeval) - # Calculate timezone offset, based on whether the local zone has - # daylight savings time, and whether DST is in effect. - if time.daylight and now[-1]: - offset = time.altzone - else: - offset = time.timezone - hours, minutes = divmod(abs(offset), 3600) - # Remember offset is in seconds west of UTC, but the timezone is in - # minutes east of UTC, so the signs differ. - if offset > 0: - sign = '-' - else: - sign = '+' - zone = '%s%02d%02d' % (sign, hours, minutes // 60) - else: - now = time.gmtime(timeval) - # Timezone offset is always -0000 - if usegmt: - zone = 'GMT' - else: - zone = '-0000' - return _format_timetuple_and_zone(now, zone) - -def format_datetime(dt, usegmt=False): - """Turn a datetime into a date string as specified in RFC 2822. - - If usegmt is True, dt must be an aware datetime with an offset of zero. In - this case 'GMT' will be rendered instead of the normal +0000 required by - RFC2822. This is to support HTTP headers involving date stamps. - """ - now = dt.timetuple() - if usegmt: - if dt.tzinfo is None or dt.tzinfo != datetime.timezone.utc: - raise ValueError("usegmt option requires a UTC datetime") - zone = 'GMT' - elif dt.tzinfo is None: - zone = '-0000' - else: - zone = dt.strftime("%z") - return _format_timetuple_and_zone(now, zone) - - -def make_msgid(idstring=None, domain=None): - """Returns a string suitable for RFC 2822 compliant Message-ID, e.g: - - <20020201195627.33539.96671@nightshade.la.mastaler.com> - - Optional idstring if given is a string used to strengthen the - uniqueness of the message id. Optional domain if given provides the - portion of the message id after the '@'. It defaults to the locally - defined hostname. - """ - timeval = time.time() - utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval)) - pid = os.getpid() - randint = random.randrange(100000) - if idstring is None: - idstring = '' - else: - idstring = '.' + idstring - if domain is None: - domain = socket.getfqdn() - msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, domain) - return msgid - - -def parsedate_to_datetime(data): - _3to2list = list(_parsedate_tz(data)) - dtuple, tz, = [_3to2list[:-1]] + _3to2list[-1:] - if tz is None: - return datetime.datetime(*dtuple[:6]) - return datetime.datetime(*dtuple[:6], - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) - - -def parseaddr(addr): - addrs = _AddressList(addr).addresslist - if not addrs: - return '', '' - return addrs[0] - - -# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3. -def unquote(str): - """Remove quotes from a string.""" - if len(str) > 1: - if str.startswith('"') and str.endswith('"'): - return str[1:-1].replace('\\\\', '\\').replace('\\"', '"') - if str.startswith('<') and str.endswith('>'): - return str[1:-1] - return str - - - -# RFC2231-related functions - parameter encoding and decoding -def decode_rfc2231(s): - """Decode string according to RFC 2231""" - parts = s.split(TICK, 2) - if len(parts) <= 2: - return None, None, s - return parts - - -def encode_rfc2231(s, charset=None, language=None): - """Encode string according to RFC 2231. - - If neither charset nor language is given, then s is returned as-is. If - charset is given but not language, the string is encoded using the empty - string for language. - """ - s = url_quote(s, safe='', encoding=charset or 'ascii') - if charset is None and language is None: - return s - if language is None: - language = '' - return "%s'%s'%s" % (charset, language, s) - - -rfc2231_continuation = re.compile(r'^(?P\w+)\*((?P[0-9]+)\*?)?$', - re.ASCII) - -def decode_params(params): - """Decode parameters list according to RFC 2231. - - params is a sequence of 2-tuples containing (param name, string value). - """ - # Copy params so we don't mess with the original - params = params[:] - new_params = [] - # Map parameter's name to a list of continuations. The values are a - # 3-tuple of the continuation number, the string value, and a flag - # specifying whether a particular segment is %-encoded. - rfc2231_params = {} - name, value = params.pop(0) - new_params.append((name, value)) - while params: - name, value = params.pop(0) - if name.endswith('*'): - encoded = True - else: - encoded = False - value = unquote(value) - mo = rfc2231_continuation.match(name) - if mo: - name, num = mo.group('name', 'num') - if num is not None: - num = int(num) - rfc2231_params.setdefault(name, []).append((num, value, encoded)) - else: - new_params.append((name, '"%s"' % quote(value))) - if rfc2231_params: - for name, continuations in rfc2231_params.items(): - value = [] - extended = False - # Sort by number - continuations.sort() - # And now append all values in numerical order, converting - # %-encodings for the encoded segments. If any of the - # continuation names ends in a *, then the entire string, after - # decoding segments and concatenating, must have the charset and - # language specifiers at the beginning of the string. - for num, s, encoded in continuations: - if encoded: - # Decode as "latin-1", so the characters in s directly - # represent the percent-encoded octet values. - # collapse_rfc2231_value treats this as an octet sequence. - s = url_unquote(s, encoding="latin-1") - extended = True - value.append(s) - value = quote(EMPTYSTRING.join(value)) - if extended: - charset, language, value = decode_rfc2231(value) - new_params.append((name, (charset, language, '"%s"' % value))) - else: - new_params.append((name, '"%s"' % value)) - return new_params - -def collapse_rfc2231_value(value, errors='replace', - fallback_charset='us-ascii'): - if not isinstance(value, tuple) or len(value) != 3: - return unquote(value) - # While value comes to us as a unicode string, we need it to be a bytes - # object. We do not want bytes() normal utf-8 decoder, we want a straight - # interpretation of the string as character bytes. - charset, language, text = value - rawbytes = bytes(text, 'raw-unicode-escape') - try: - return str(rawbytes, charset, errors) - except LookupError: - # charset is not a known codec. - return unquote(text) - - -# -# datetime doesn't provide a localtime function yet, so provide one. Code -# adapted from the patch in issue 9527. This may not be perfect, but it is -# better than not having it. -# - -def localtime(dt=None, isdst=-1): - """Return local time as an aware datetime object. - - If called without arguments, return current time. Otherwise *dt* - argument should be a datetime instance, and it is converted to the - local time zone according to the system time zone database. If *dt* is - naive (that is, dt.tzinfo is None), it is assumed to be in local time. - In this case, a positive or zero value for *isdst* causes localtime to - presume initially that summer time (for example, Daylight Saving Time) - is or is not (respectively) in effect for the specified time. A - negative value for *isdst* causes the localtime() function to attempt - to divine whether summer time is in effect for the specified time. - - """ - if dt is None: - return datetime.datetime.now(datetime.timezone.utc).astimezone() - if dt.tzinfo is not None: - return dt.astimezone() - # We have a naive datetime. Convert to a (localtime) timetuple and pass to - # system mktime together with the isdst hint. System mktime will return - # seconds since epoch. - tm = dt.timetuple()[:-1] + (isdst,) - seconds = time.mktime(tm) - localtm = time.localtime(seconds) - try: - delta = datetime.timedelta(seconds=localtm.tm_gmtoff) - tz = datetime.timezone(delta, localtm.tm_zone) - except AttributeError: - # Compute UTC offset and compare with the value implied by tm_isdst. - # If the values match, use the zone name implied by tm_isdst. - delta = dt - datetime.datetime(*time.gmtime(seconds)[:6]) - dst = time.daylight and localtm.tm_isdst > 0 - gmtoff = -(time.altzone if dst else time.timezone) - if delta == datetime.timedelta(seconds=gmtoff): - tz = datetime.timezone(delta, time.tzname[dst]) - else: - tz = datetime.timezone(delta) - return dt.replace(tzinfo=tz) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/html/entities.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/html/entities.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/html/entities.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/html/entities.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2515 +0,0 @@ -"""HTML character entity references. - -Backported for python-future from Python 3.3 -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import * - - -# maps the HTML entity name to the Unicode codepoint -name2codepoint = { - 'AElig': 0x00c6, # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 - 'Aacute': 0x00c1, # latin capital letter A with acute, U+00C1 ISOlat1 - 'Acirc': 0x00c2, # latin capital letter A with circumflex, U+00C2 ISOlat1 - 'Agrave': 0x00c0, # latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 - 'Alpha': 0x0391, # greek capital letter alpha, U+0391 - 'Aring': 0x00c5, # latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 - 'Atilde': 0x00c3, # latin capital letter A with tilde, U+00C3 ISOlat1 - 'Auml': 0x00c4, # latin capital letter A with diaeresis, U+00C4 ISOlat1 - 'Beta': 0x0392, # greek capital letter beta, U+0392 - 'Ccedil': 0x00c7, # latin capital letter C with cedilla, U+00C7 ISOlat1 - 'Chi': 0x03a7, # greek capital letter chi, U+03A7 - 'Dagger': 0x2021, # double dagger, U+2021 ISOpub - 'Delta': 0x0394, # greek capital letter delta, U+0394 ISOgrk3 - 'ETH': 0x00d0, # latin capital letter ETH, U+00D0 ISOlat1 - 'Eacute': 0x00c9, # latin capital letter E with acute, U+00C9 ISOlat1 - 'Ecirc': 0x00ca, # latin capital letter E with circumflex, U+00CA ISOlat1 - 'Egrave': 0x00c8, # latin capital letter E with grave, U+00C8 ISOlat1 - 'Epsilon': 0x0395, # greek capital letter epsilon, U+0395 - 'Eta': 0x0397, # greek capital letter eta, U+0397 - 'Euml': 0x00cb, # latin capital letter E with diaeresis, U+00CB ISOlat1 - 'Gamma': 0x0393, # greek capital letter gamma, U+0393 ISOgrk3 - 'Iacute': 0x00cd, # latin capital letter I with acute, U+00CD ISOlat1 - 'Icirc': 0x00ce, # latin capital letter I with circumflex, U+00CE ISOlat1 - 'Igrave': 0x00cc, # latin capital letter I with grave, U+00CC ISOlat1 - 'Iota': 0x0399, # greek capital letter iota, U+0399 - 'Iuml': 0x00cf, # latin capital letter I with diaeresis, U+00CF ISOlat1 - 'Kappa': 0x039a, # greek capital letter kappa, U+039A - 'Lambda': 0x039b, # greek capital letter lambda, U+039B ISOgrk3 - 'Mu': 0x039c, # greek capital letter mu, U+039C - 'Ntilde': 0x00d1, # latin capital letter N with tilde, U+00D1 ISOlat1 - 'Nu': 0x039d, # greek capital letter nu, U+039D - 'OElig': 0x0152, # latin capital ligature OE, U+0152 ISOlat2 - 'Oacute': 0x00d3, # latin capital letter O with acute, U+00D3 ISOlat1 - 'Ocirc': 0x00d4, # latin capital letter O with circumflex, U+00D4 ISOlat1 - 'Ograve': 0x00d2, # latin capital letter O with grave, U+00D2 ISOlat1 - 'Omega': 0x03a9, # greek capital letter omega, U+03A9 ISOgrk3 - 'Omicron': 0x039f, # greek capital letter omicron, U+039F - 'Oslash': 0x00d8, # latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 - 'Otilde': 0x00d5, # latin capital letter O with tilde, U+00D5 ISOlat1 - 'Ouml': 0x00d6, # latin capital letter O with diaeresis, U+00D6 ISOlat1 - 'Phi': 0x03a6, # greek capital letter phi, U+03A6 ISOgrk3 - 'Pi': 0x03a0, # greek capital letter pi, U+03A0 ISOgrk3 - 'Prime': 0x2033, # double prime = seconds = inches, U+2033 ISOtech - 'Psi': 0x03a8, # greek capital letter psi, U+03A8 ISOgrk3 - 'Rho': 0x03a1, # greek capital letter rho, U+03A1 - 'Scaron': 0x0160, # latin capital letter S with caron, U+0160 ISOlat2 - 'Sigma': 0x03a3, # greek capital letter sigma, U+03A3 ISOgrk3 - 'THORN': 0x00de, # latin capital letter THORN, U+00DE ISOlat1 - 'Tau': 0x03a4, # greek capital letter tau, U+03A4 - 'Theta': 0x0398, # greek capital letter theta, U+0398 ISOgrk3 - 'Uacute': 0x00da, # latin capital letter U with acute, U+00DA ISOlat1 - 'Ucirc': 0x00db, # latin capital letter U with circumflex, U+00DB ISOlat1 - 'Ugrave': 0x00d9, # latin capital letter U with grave, U+00D9 ISOlat1 - 'Upsilon': 0x03a5, # greek capital letter upsilon, U+03A5 ISOgrk3 - 'Uuml': 0x00dc, # latin capital letter U with diaeresis, U+00DC ISOlat1 - 'Xi': 0x039e, # greek capital letter xi, U+039E ISOgrk3 - 'Yacute': 0x00dd, # latin capital letter Y with acute, U+00DD ISOlat1 - 'Yuml': 0x0178, # latin capital letter Y with diaeresis, U+0178 ISOlat2 - 'Zeta': 0x0396, # greek capital letter zeta, U+0396 - 'aacute': 0x00e1, # latin small letter a with acute, U+00E1 ISOlat1 - 'acirc': 0x00e2, # latin small letter a with circumflex, U+00E2 ISOlat1 - 'acute': 0x00b4, # acute accent = spacing acute, U+00B4 ISOdia - 'aelig': 0x00e6, # latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 - 'agrave': 0x00e0, # latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 - 'alefsym': 0x2135, # alef symbol = first transfinite cardinal, U+2135 NEW - 'alpha': 0x03b1, # greek small letter alpha, U+03B1 ISOgrk3 - 'amp': 0x0026, # ampersand, U+0026 ISOnum - 'and': 0x2227, # logical and = wedge, U+2227 ISOtech - 'ang': 0x2220, # angle, U+2220 ISOamso - 'aring': 0x00e5, # latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 - 'asymp': 0x2248, # almost equal to = asymptotic to, U+2248 ISOamsr - 'atilde': 0x00e3, # latin small letter a with tilde, U+00E3 ISOlat1 - 'auml': 0x00e4, # latin small letter a with diaeresis, U+00E4 ISOlat1 - 'bdquo': 0x201e, # double low-9 quotation mark, U+201E NEW - 'beta': 0x03b2, # greek small letter beta, U+03B2 ISOgrk3 - 'brvbar': 0x00a6, # broken bar = broken vertical bar, U+00A6 ISOnum - 'bull': 0x2022, # bullet = black small circle, U+2022 ISOpub - 'cap': 0x2229, # intersection = cap, U+2229 ISOtech - 'ccedil': 0x00e7, # latin small letter c with cedilla, U+00E7 ISOlat1 - 'cedil': 0x00b8, # cedilla = spacing cedilla, U+00B8 ISOdia - 'cent': 0x00a2, # cent sign, U+00A2 ISOnum - 'chi': 0x03c7, # greek small letter chi, U+03C7 ISOgrk3 - 'circ': 0x02c6, # modifier letter circumflex accent, U+02C6 ISOpub - 'clubs': 0x2663, # black club suit = shamrock, U+2663 ISOpub - 'cong': 0x2245, # approximately equal to, U+2245 ISOtech - 'copy': 0x00a9, # copyright sign, U+00A9 ISOnum - 'crarr': 0x21b5, # downwards arrow with corner leftwards = carriage return, U+21B5 NEW - 'cup': 0x222a, # union = cup, U+222A ISOtech - 'curren': 0x00a4, # currency sign, U+00A4 ISOnum - 'dArr': 0x21d3, # downwards double arrow, U+21D3 ISOamsa - 'dagger': 0x2020, # dagger, U+2020 ISOpub - 'darr': 0x2193, # downwards arrow, U+2193 ISOnum - 'deg': 0x00b0, # degree sign, U+00B0 ISOnum - 'delta': 0x03b4, # greek small letter delta, U+03B4 ISOgrk3 - 'diams': 0x2666, # black diamond suit, U+2666 ISOpub - 'divide': 0x00f7, # division sign, U+00F7 ISOnum - 'eacute': 0x00e9, # latin small letter e with acute, U+00E9 ISOlat1 - 'ecirc': 0x00ea, # latin small letter e with circumflex, U+00EA ISOlat1 - 'egrave': 0x00e8, # latin small letter e with grave, U+00E8 ISOlat1 - 'empty': 0x2205, # empty set = null set = diameter, U+2205 ISOamso - 'emsp': 0x2003, # em space, U+2003 ISOpub - 'ensp': 0x2002, # en space, U+2002 ISOpub - 'epsilon': 0x03b5, # greek small letter epsilon, U+03B5 ISOgrk3 - 'equiv': 0x2261, # identical to, U+2261 ISOtech - 'eta': 0x03b7, # greek small letter eta, U+03B7 ISOgrk3 - 'eth': 0x00f0, # latin small letter eth, U+00F0 ISOlat1 - 'euml': 0x00eb, # latin small letter e with diaeresis, U+00EB ISOlat1 - 'euro': 0x20ac, # euro sign, U+20AC NEW - 'exist': 0x2203, # there exists, U+2203 ISOtech - 'fnof': 0x0192, # latin small f with hook = function = florin, U+0192 ISOtech - 'forall': 0x2200, # for all, U+2200 ISOtech - 'frac12': 0x00bd, # vulgar fraction one half = fraction one half, U+00BD ISOnum - 'frac14': 0x00bc, # vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum - 'frac34': 0x00be, # vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum - 'frasl': 0x2044, # fraction slash, U+2044 NEW - 'gamma': 0x03b3, # greek small letter gamma, U+03B3 ISOgrk3 - 'ge': 0x2265, # greater-than or equal to, U+2265 ISOtech - 'gt': 0x003e, # greater-than sign, U+003E ISOnum - 'hArr': 0x21d4, # left right double arrow, U+21D4 ISOamsa - 'harr': 0x2194, # left right arrow, U+2194 ISOamsa - 'hearts': 0x2665, # black heart suit = valentine, U+2665 ISOpub - 'hellip': 0x2026, # horizontal ellipsis = three dot leader, U+2026 ISOpub - 'iacute': 0x00ed, # latin small letter i with acute, U+00ED ISOlat1 - 'icirc': 0x00ee, # latin small letter i with circumflex, U+00EE ISOlat1 - 'iexcl': 0x00a1, # inverted exclamation mark, U+00A1 ISOnum - 'igrave': 0x00ec, # latin small letter i with grave, U+00EC ISOlat1 - 'image': 0x2111, # blackletter capital I = imaginary part, U+2111 ISOamso - 'infin': 0x221e, # infinity, U+221E ISOtech - 'int': 0x222b, # integral, U+222B ISOtech - 'iota': 0x03b9, # greek small letter iota, U+03B9 ISOgrk3 - 'iquest': 0x00bf, # inverted question mark = turned question mark, U+00BF ISOnum - 'isin': 0x2208, # element of, U+2208 ISOtech - 'iuml': 0x00ef, # latin small letter i with diaeresis, U+00EF ISOlat1 - 'kappa': 0x03ba, # greek small letter kappa, U+03BA ISOgrk3 - 'lArr': 0x21d0, # leftwards double arrow, U+21D0 ISOtech - 'lambda': 0x03bb, # greek small letter lambda, U+03BB ISOgrk3 - 'lang': 0x2329, # left-pointing angle bracket = bra, U+2329 ISOtech - 'laquo': 0x00ab, # left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum - 'larr': 0x2190, # leftwards arrow, U+2190 ISOnum - 'lceil': 0x2308, # left ceiling = apl upstile, U+2308 ISOamsc - 'ldquo': 0x201c, # left double quotation mark, U+201C ISOnum - 'le': 0x2264, # less-than or equal to, U+2264 ISOtech - 'lfloor': 0x230a, # left floor = apl downstile, U+230A ISOamsc - 'lowast': 0x2217, # asterisk operator, U+2217 ISOtech - 'loz': 0x25ca, # lozenge, U+25CA ISOpub - 'lrm': 0x200e, # left-to-right mark, U+200E NEW RFC 2070 - 'lsaquo': 0x2039, # single left-pointing angle quotation mark, U+2039 ISO proposed - 'lsquo': 0x2018, # left single quotation mark, U+2018 ISOnum - 'lt': 0x003c, # less-than sign, U+003C ISOnum - 'macr': 0x00af, # macron = spacing macron = overline = APL overbar, U+00AF ISOdia - 'mdash': 0x2014, # em dash, U+2014 ISOpub - 'micro': 0x00b5, # micro sign, U+00B5 ISOnum - 'middot': 0x00b7, # middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum - 'minus': 0x2212, # minus sign, U+2212 ISOtech - 'mu': 0x03bc, # greek small letter mu, U+03BC ISOgrk3 - 'nabla': 0x2207, # nabla = backward difference, U+2207 ISOtech - 'nbsp': 0x00a0, # no-break space = non-breaking space, U+00A0 ISOnum - 'ndash': 0x2013, # en dash, U+2013 ISOpub - 'ne': 0x2260, # not equal to, U+2260 ISOtech - 'ni': 0x220b, # contains as member, U+220B ISOtech - 'not': 0x00ac, # not sign, U+00AC ISOnum - 'notin': 0x2209, # not an element of, U+2209 ISOtech - 'nsub': 0x2284, # not a subset of, U+2284 ISOamsn - 'ntilde': 0x00f1, # latin small letter n with tilde, U+00F1 ISOlat1 - 'nu': 0x03bd, # greek small letter nu, U+03BD ISOgrk3 - 'oacute': 0x00f3, # latin small letter o with acute, U+00F3 ISOlat1 - 'ocirc': 0x00f4, # latin small letter o with circumflex, U+00F4 ISOlat1 - 'oelig': 0x0153, # latin small ligature oe, U+0153 ISOlat2 - 'ograve': 0x00f2, # latin small letter o with grave, U+00F2 ISOlat1 - 'oline': 0x203e, # overline = spacing overscore, U+203E NEW - 'omega': 0x03c9, # greek small letter omega, U+03C9 ISOgrk3 - 'omicron': 0x03bf, # greek small letter omicron, U+03BF NEW - 'oplus': 0x2295, # circled plus = direct sum, U+2295 ISOamsb - 'or': 0x2228, # logical or = vee, U+2228 ISOtech - 'ordf': 0x00aa, # feminine ordinal indicator, U+00AA ISOnum - 'ordm': 0x00ba, # masculine ordinal indicator, U+00BA ISOnum - 'oslash': 0x00f8, # latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 - 'otilde': 0x00f5, # latin small letter o with tilde, U+00F5 ISOlat1 - 'otimes': 0x2297, # circled times = vector product, U+2297 ISOamsb - 'ouml': 0x00f6, # latin small letter o with diaeresis, U+00F6 ISOlat1 - 'para': 0x00b6, # pilcrow sign = paragraph sign, U+00B6 ISOnum - 'part': 0x2202, # partial differential, U+2202 ISOtech - 'permil': 0x2030, # per mille sign, U+2030 ISOtech - 'perp': 0x22a5, # up tack = orthogonal to = perpendicular, U+22A5 ISOtech - 'phi': 0x03c6, # greek small letter phi, U+03C6 ISOgrk3 - 'pi': 0x03c0, # greek small letter pi, U+03C0 ISOgrk3 - 'piv': 0x03d6, # greek pi symbol, U+03D6 ISOgrk3 - 'plusmn': 0x00b1, # plus-minus sign = plus-or-minus sign, U+00B1 ISOnum - 'pound': 0x00a3, # pound sign, U+00A3 ISOnum - 'prime': 0x2032, # prime = minutes = feet, U+2032 ISOtech - 'prod': 0x220f, # n-ary product = product sign, U+220F ISOamsb - 'prop': 0x221d, # proportional to, U+221D ISOtech - 'psi': 0x03c8, # greek small letter psi, U+03C8 ISOgrk3 - 'quot': 0x0022, # quotation mark = APL quote, U+0022 ISOnum - 'rArr': 0x21d2, # rightwards double arrow, U+21D2 ISOtech - 'radic': 0x221a, # square root = radical sign, U+221A ISOtech - 'rang': 0x232a, # right-pointing angle bracket = ket, U+232A ISOtech - 'raquo': 0x00bb, # right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum - 'rarr': 0x2192, # rightwards arrow, U+2192 ISOnum - 'rceil': 0x2309, # right ceiling, U+2309 ISOamsc - 'rdquo': 0x201d, # right double quotation mark, U+201D ISOnum - 'real': 0x211c, # blackletter capital R = real part symbol, U+211C ISOamso - 'reg': 0x00ae, # registered sign = registered trade mark sign, U+00AE ISOnum - 'rfloor': 0x230b, # right floor, U+230B ISOamsc - 'rho': 0x03c1, # greek small letter rho, U+03C1 ISOgrk3 - 'rlm': 0x200f, # right-to-left mark, U+200F NEW RFC 2070 - 'rsaquo': 0x203a, # single right-pointing angle quotation mark, U+203A ISO proposed - 'rsquo': 0x2019, # right single quotation mark, U+2019 ISOnum - 'sbquo': 0x201a, # single low-9 quotation mark, U+201A NEW - 'scaron': 0x0161, # latin small letter s with caron, U+0161 ISOlat2 - 'sdot': 0x22c5, # dot operator, U+22C5 ISOamsb - 'sect': 0x00a7, # section sign, U+00A7 ISOnum - 'shy': 0x00ad, # soft hyphen = discretionary hyphen, U+00AD ISOnum - 'sigma': 0x03c3, # greek small letter sigma, U+03C3 ISOgrk3 - 'sigmaf': 0x03c2, # greek small letter final sigma, U+03C2 ISOgrk3 - 'sim': 0x223c, # tilde operator = varies with = similar to, U+223C ISOtech - 'spades': 0x2660, # black spade suit, U+2660 ISOpub - 'sub': 0x2282, # subset of, U+2282 ISOtech - 'sube': 0x2286, # subset of or equal to, U+2286 ISOtech - 'sum': 0x2211, # n-ary sumation, U+2211 ISOamsb - 'sup': 0x2283, # superset of, U+2283 ISOtech - 'sup1': 0x00b9, # superscript one = superscript digit one, U+00B9 ISOnum - 'sup2': 0x00b2, # superscript two = superscript digit two = squared, U+00B2 ISOnum - 'sup3': 0x00b3, # superscript three = superscript digit three = cubed, U+00B3 ISOnum - 'supe': 0x2287, # superset of or equal to, U+2287 ISOtech - 'szlig': 0x00df, # latin small letter sharp s = ess-zed, U+00DF ISOlat1 - 'tau': 0x03c4, # greek small letter tau, U+03C4 ISOgrk3 - 'there4': 0x2234, # therefore, U+2234 ISOtech - 'theta': 0x03b8, # greek small letter theta, U+03B8 ISOgrk3 - 'thetasym': 0x03d1, # greek small letter theta symbol, U+03D1 NEW - 'thinsp': 0x2009, # thin space, U+2009 ISOpub - 'thorn': 0x00fe, # latin small letter thorn with, U+00FE ISOlat1 - 'tilde': 0x02dc, # small tilde, U+02DC ISOdia - 'times': 0x00d7, # multiplication sign, U+00D7 ISOnum - 'trade': 0x2122, # trade mark sign, U+2122 ISOnum - 'uArr': 0x21d1, # upwards double arrow, U+21D1 ISOamsa - 'uacute': 0x00fa, # latin small letter u with acute, U+00FA ISOlat1 - 'uarr': 0x2191, # upwards arrow, U+2191 ISOnum - 'ucirc': 0x00fb, # latin small letter u with circumflex, U+00FB ISOlat1 - 'ugrave': 0x00f9, # latin small letter u with grave, U+00F9 ISOlat1 - 'uml': 0x00a8, # diaeresis = spacing diaeresis, U+00A8 ISOdia - 'upsih': 0x03d2, # greek upsilon with hook symbol, U+03D2 NEW - 'upsilon': 0x03c5, # greek small letter upsilon, U+03C5 ISOgrk3 - 'uuml': 0x00fc, # latin small letter u with diaeresis, U+00FC ISOlat1 - 'weierp': 0x2118, # script capital P = power set = Weierstrass p, U+2118 ISOamso - 'xi': 0x03be, # greek small letter xi, U+03BE ISOgrk3 - 'yacute': 0x00fd, # latin small letter y with acute, U+00FD ISOlat1 - 'yen': 0x00a5, # yen sign = yuan sign, U+00A5 ISOnum - 'yuml': 0x00ff, # latin small letter y with diaeresis, U+00FF ISOlat1 - 'zeta': 0x03b6, # greek small letter zeta, U+03B6 ISOgrk3 - 'zwj': 0x200d, # zero width joiner, U+200D NEW RFC 2070 - 'zwnj': 0x200c, # zero width non-joiner, U+200C NEW RFC 2070 -} - - -# maps the HTML5 named character references to the equivalent Unicode character(s) -html5 = { - 'Aacute': '\xc1', - 'aacute': '\xe1', - 'Aacute;': '\xc1', - 'aacute;': '\xe1', - 'Abreve;': '\u0102', - 'abreve;': '\u0103', - 'ac;': '\u223e', - 'acd;': '\u223f', - 'acE;': '\u223e\u0333', - 'Acirc': '\xc2', - 'acirc': '\xe2', - 'Acirc;': '\xc2', - 'acirc;': '\xe2', - 'acute': '\xb4', - 'acute;': '\xb4', - 'Acy;': '\u0410', - 'acy;': '\u0430', - 'AElig': '\xc6', - 'aelig': '\xe6', - 'AElig;': '\xc6', - 'aelig;': '\xe6', - 'af;': '\u2061', - 'Afr;': '\U0001d504', - 'afr;': '\U0001d51e', - 'Agrave': '\xc0', - 'agrave': '\xe0', - 'Agrave;': '\xc0', - 'agrave;': '\xe0', - 'alefsym;': '\u2135', - 'aleph;': '\u2135', - 'Alpha;': '\u0391', - 'alpha;': '\u03b1', - 'Amacr;': '\u0100', - 'amacr;': '\u0101', - 'amalg;': '\u2a3f', - 'AMP': '&', - 'amp': '&', - 'AMP;': '&', - 'amp;': '&', - 'And;': '\u2a53', - 'and;': '\u2227', - 'andand;': '\u2a55', - 'andd;': '\u2a5c', - 'andslope;': '\u2a58', - 'andv;': '\u2a5a', - 'ang;': '\u2220', - 'ange;': '\u29a4', - 'angle;': '\u2220', - 'angmsd;': '\u2221', - 'angmsdaa;': '\u29a8', - 'angmsdab;': '\u29a9', - 'angmsdac;': '\u29aa', - 'angmsdad;': '\u29ab', - 'angmsdae;': '\u29ac', - 'angmsdaf;': '\u29ad', - 'angmsdag;': '\u29ae', - 'angmsdah;': '\u29af', - 'angrt;': '\u221f', - 'angrtvb;': '\u22be', - 'angrtvbd;': '\u299d', - 'angsph;': '\u2222', - 'angst;': '\xc5', - 'angzarr;': '\u237c', - 'Aogon;': '\u0104', - 'aogon;': '\u0105', - 'Aopf;': '\U0001d538', - 'aopf;': '\U0001d552', - 'ap;': '\u2248', - 'apacir;': '\u2a6f', - 'apE;': '\u2a70', - 'ape;': '\u224a', - 'apid;': '\u224b', - 'apos;': "'", - 'ApplyFunction;': '\u2061', - 'approx;': '\u2248', - 'approxeq;': '\u224a', - 'Aring': '\xc5', - 'aring': '\xe5', - 'Aring;': '\xc5', - 'aring;': '\xe5', - 'Ascr;': '\U0001d49c', - 'ascr;': '\U0001d4b6', - 'Assign;': '\u2254', - 'ast;': '*', - 'asymp;': '\u2248', - 'asympeq;': '\u224d', - 'Atilde': '\xc3', - 'atilde': '\xe3', - 'Atilde;': '\xc3', - 'atilde;': '\xe3', - 'Auml': '\xc4', - 'auml': '\xe4', - 'Auml;': '\xc4', - 'auml;': '\xe4', - 'awconint;': '\u2233', - 'awint;': '\u2a11', - 'backcong;': '\u224c', - 'backepsilon;': '\u03f6', - 'backprime;': '\u2035', - 'backsim;': '\u223d', - 'backsimeq;': '\u22cd', - 'Backslash;': '\u2216', - 'Barv;': '\u2ae7', - 'barvee;': '\u22bd', - 'Barwed;': '\u2306', - 'barwed;': '\u2305', - 'barwedge;': '\u2305', - 'bbrk;': '\u23b5', - 'bbrktbrk;': '\u23b6', - 'bcong;': '\u224c', - 'Bcy;': '\u0411', - 'bcy;': '\u0431', - 'bdquo;': '\u201e', - 'becaus;': '\u2235', - 'Because;': '\u2235', - 'because;': '\u2235', - 'bemptyv;': '\u29b0', - 'bepsi;': '\u03f6', - 'bernou;': '\u212c', - 'Bernoullis;': '\u212c', - 'Beta;': '\u0392', - 'beta;': '\u03b2', - 'beth;': '\u2136', - 'between;': '\u226c', - 'Bfr;': '\U0001d505', - 'bfr;': '\U0001d51f', - 'bigcap;': '\u22c2', - 'bigcirc;': '\u25ef', - 'bigcup;': '\u22c3', - 'bigodot;': '\u2a00', - 'bigoplus;': '\u2a01', - 'bigotimes;': '\u2a02', - 'bigsqcup;': '\u2a06', - 'bigstar;': '\u2605', - 'bigtriangledown;': '\u25bd', - 'bigtriangleup;': '\u25b3', - 'biguplus;': '\u2a04', - 'bigvee;': '\u22c1', - 'bigwedge;': '\u22c0', - 'bkarow;': '\u290d', - 'blacklozenge;': '\u29eb', - 'blacksquare;': '\u25aa', - 'blacktriangle;': '\u25b4', - 'blacktriangledown;': '\u25be', - 'blacktriangleleft;': '\u25c2', - 'blacktriangleright;': '\u25b8', - 'blank;': '\u2423', - 'blk12;': '\u2592', - 'blk14;': '\u2591', - 'blk34;': '\u2593', - 'block;': '\u2588', - 'bne;': '=\u20e5', - 'bnequiv;': '\u2261\u20e5', - 'bNot;': '\u2aed', - 'bnot;': '\u2310', - 'Bopf;': '\U0001d539', - 'bopf;': '\U0001d553', - 'bot;': '\u22a5', - 'bottom;': '\u22a5', - 'bowtie;': '\u22c8', - 'boxbox;': '\u29c9', - 'boxDL;': '\u2557', - 'boxDl;': '\u2556', - 'boxdL;': '\u2555', - 'boxdl;': '\u2510', - 'boxDR;': '\u2554', - 'boxDr;': '\u2553', - 'boxdR;': '\u2552', - 'boxdr;': '\u250c', - 'boxH;': '\u2550', - 'boxh;': '\u2500', - 'boxHD;': '\u2566', - 'boxHd;': '\u2564', - 'boxhD;': '\u2565', - 'boxhd;': '\u252c', - 'boxHU;': '\u2569', - 'boxHu;': '\u2567', - 'boxhU;': '\u2568', - 'boxhu;': '\u2534', - 'boxminus;': '\u229f', - 'boxplus;': '\u229e', - 'boxtimes;': '\u22a0', - 'boxUL;': '\u255d', - 'boxUl;': '\u255c', - 'boxuL;': '\u255b', - 'boxul;': '\u2518', - 'boxUR;': '\u255a', - 'boxUr;': '\u2559', - 'boxuR;': '\u2558', - 'boxur;': '\u2514', - 'boxV;': '\u2551', - 'boxv;': '\u2502', - 'boxVH;': '\u256c', - 'boxVh;': '\u256b', - 'boxvH;': '\u256a', - 'boxvh;': '\u253c', - 'boxVL;': '\u2563', - 'boxVl;': '\u2562', - 'boxvL;': '\u2561', - 'boxvl;': '\u2524', - 'boxVR;': '\u2560', - 'boxVr;': '\u255f', - 'boxvR;': '\u255e', - 'boxvr;': '\u251c', - 'bprime;': '\u2035', - 'Breve;': '\u02d8', - 'breve;': '\u02d8', - 'brvbar': '\xa6', - 'brvbar;': '\xa6', - 'Bscr;': '\u212c', - 'bscr;': '\U0001d4b7', - 'bsemi;': '\u204f', - 'bsim;': '\u223d', - 'bsime;': '\u22cd', - 'bsol;': '\\', - 'bsolb;': '\u29c5', - 'bsolhsub;': '\u27c8', - 'bull;': '\u2022', - 'bullet;': '\u2022', - 'bump;': '\u224e', - 'bumpE;': '\u2aae', - 'bumpe;': '\u224f', - 'Bumpeq;': '\u224e', - 'bumpeq;': '\u224f', - 'Cacute;': '\u0106', - 'cacute;': '\u0107', - 'Cap;': '\u22d2', - 'cap;': '\u2229', - 'capand;': '\u2a44', - 'capbrcup;': '\u2a49', - 'capcap;': '\u2a4b', - 'capcup;': '\u2a47', - 'capdot;': '\u2a40', - 'CapitalDifferentialD;': '\u2145', - 'caps;': '\u2229\ufe00', - 'caret;': '\u2041', - 'caron;': '\u02c7', - 'Cayleys;': '\u212d', - 'ccaps;': '\u2a4d', - 'Ccaron;': '\u010c', - 'ccaron;': '\u010d', - 'Ccedil': '\xc7', - 'ccedil': '\xe7', - 'Ccedil;': '\xc7', - 'ccedil;': '\xe7', - 'Ccirc;': '\u0108', - 'ccirc;': '\u0109', - 'Cconint;': '\u2230', - 'ccups;': '\u2a4c', - 'ccupssm;': '\u2a50', - 'Cdot;': '\u010a', - 'cdot;': '\u010b', - 'cedil': '\xb8', - 'cedil;': '\xb8', - 'Cedilla;': '\xb8', - 'cemptyv;': '\u29b2', - 'cent': '\xa2', - 'cent;': '\xa2', - 'CenterDot;': '\xb7', - 'centerdot;': '\xb7', - 'Cfr;': '\u212d', - 'cfr;': '\U0001d520', - 'CHcy;': '\u0427', - 'chcy;': '\u0447', - 'check;': '\u2713', - 'checkmark;': '\u2713', - 'Chi;': '\u03a7', - 'chi;': '\u03c7', - 'cir;': '\u25cb', - 'circ;': '\u02c6', - 'circeq;': '\u2257', - 'circlearrowleft;': '\u21ba', - 'circlearrowright;': '\u21bb', - 'circledast;': '\u229b', - 'circledcirc;': '\u229a', - 'circleddash;': '\u229d', - 'CircleDot;': '\u2299', - 'circledR;': '\xae', - 'circledS;': '\u24c8', - 'CircleMinus;': '\u2296', - 'CirclePlus;': '\u2295', - 'CircleTimes;': '\u2297', - 'cirE;': '\u29c3', - 'cire;': '\u2257', - 'cirfnint;': '\u2a10', - 'cirmid;': '\u2aef', - 'cirscir;': '\u29c2', - 'ClockwiseContourIntegral;': '\u2232', - 'CloseCurlyDoubleQuote;': '\u201d', - 'CloseCurlyQuote;': '\u2019', - 'clubs;': '\u2663', - 'clubsuit;': '\u2663', - 'Colon;': '\u2237', - 'colon;': ':', - 'Colone;': '\u2a74', - 'colone;': '\u2254', - 'coloneq;': '\u2254', - 'comma;': ',', - 'commat;': '@', - 'comp;': '\u2201', - 'compfn;': '\u2218', - 'complement;': '\u2201', - 'complexes;': '\u2102', - 'cong;': '\u2245', - 'congdot;': '\u2a6d', - 'Congruent;': '\u2261', - 'Conint;': '\u222f', - 'conint;': '\u222e', - 'ContourIntegral;': '\u222e', - 'Copf;': '\u2102', - 'copf;': '\U0001d554', - 'coprod;': '\u2210', - 'Coproduct;': '\u2210', - 'COPY': '\xa9', - 'copy': '\xa9', - 'COPY;': '\xa9', - 'copy;': '\xa9', - 'copysr;': '\u2117', - 'CounterClockwiseContourIntegral;': '\u2233', - 'crarr;': '\u21b5', - 'Cross;': '\u2a2f', - 'cross;': '\u2717', - 'Cscr;': '\U0001d49e', - 'cscr;': '\U0001d4b8', - 'csub;': '\u2acf', - 'csube;': '\u2ad1', - 'csup;': '\u2ad0', - 'csupe;': '\u2ad2', - 'ctdot;': '\u22ef', - 'cudarrl;': '\u2938', - 'cudarrr;': '\u2935', - 'cuepr;': '\u22de', - 'cuesc;': '\u22df', - 'cularr;': '\u21b6', - 'cularrp;': '\u293d', - 'Cup;': '\u22d3', - 'cup;': '\u222a', - 'cupbrcap;': '\u2a48', - 'CupCap;': '\u224d', - 'cupcap;': '\u2a46', - 'cupcup;': '\u2a4a', - 'cupdot;': '\u228d', - 'cupor;': '\u2a45', - 'cups;': '\u222a\ufe00', - 'curarr;': '\u21b7', - 'curarrm;': '\u293c', - 'curlyeqprec;': '\u22de', - 'curlyeqsucc;': '\u22df', - 'curlyvee;': '\u22ce', - 'curlywedge;': '\u22cf', - 'curren': '\xa4', - 'curren;': '\xa4', - 'curvearrowleft;': '\u21b6', - 'curvearrowright;': '\u21b7', - 'cuvee;': '\u22ce', - 'cuwed;': '\u22cf', - 'cwconint;': '\u2232', - 'cwint;': '\u2231', - 'cylcty;': '\u232d', - 'Dagger;': '\u2021', - 'dagger;': '\u2020', - 'daleth;': '\u2138', - 'Darr;': '\u21a1', - 'dArr;': '\u21d3', - 'darr;': '\u2193', - 'dash;': '\u2010', - 'Dashv;': '\u2ae4', - 'dashv;': '\u22a3', - 'dbkarow;': '\u290f', - 'dblac;': '\u02dd', - 'Dcaron;': '\u010e', - 'dcaron;': '\u010f', - 'Dcy;': '\u0414', - 'dcy;': '\u0434', - 'DD;': '\u2145', - 'dd;': '\u2146', - 'ddagger;': '\u2021', - 'ddarr;': '\u21ca', - 'DDotrahd;': '\u2911', - 'ddotseq;': '\u2a77', - 'deg': '\xb0', - 'deg;': '\xb0', - 'Del;': '\u2207', - 'Delta;': '\u0394', - 'delta;': '\u03b4', - 'demptyv;': '\u29b1', - 'dfisht;': '\u297f', - 'Dfr;': '\U0001d507', - 'dfr;': '\U0001d521', - 'dHar;': '\u2965', - 'dharl;': '\u21c3', - 'dharr;': '\u21c2', - 'DiacriticalAcute;': '\xb4', - 'DiacriticalDot;': '\u02d9', - 'DiacriticalDoubleAcute;': '\u02dd', - 'DiacriticalGrave;': '`', - 'DiacriticalTilde;': '\u02dc', - 'diam;': '\u22c4', - 'Diamond;': '\u22c4', - 'diamond;': '\u22c4', - 'diamondsuit;': '\u2666', - 'diams;': '\u2666', - 'die;': '\xa8', - 'DifferentialD;': '\u2146', - 'digamma;': '\u03dd', - 'disin;': '\u22f2', - 'div;': '\xf7', - 'divide': '\xf7', - 'divide;': '\xf7', - 'divideontimes;': '\u22c7', - 'divonx;': '\u22c7', - 'DJcy;': '\u0402', - 'djcy;': '\u0452', - 'dlcorn;': '\u231e', - 'dlcrop;': '\u230d', - 'dollar;': '$', - 'Dopf;': '\U0001d53b', - 'dopf;': '\U0001d555', - 'Dot;': '\xa8', - 'dot;': '\u02d9', - 'DotDot;': '\u20dc', - 'doteq;': '\u2250', - 'doteqdot;': '\u2251', - 'DotEqual;': '\u2250', - 'dotminus;': '\u2238', - 'dotplus;': '\u2214', - 'dotsquare;': '\u22a1', - 'doublebarwedge;': '\u2306', - 'DoubleContourIntegral;': '\u222f', - 'DoubleDot;': '\xa8', - 'DoubleDownArrow;': '\u21d3', - 'DoubleLeftArrow;': '\u21d0', - 'DoubleLeftRightArrow;': '\u21d4', - 'DoubleLeftTee;': '\u2ae4', - 'DoubleLongLeftArrow;': '\u27f8', - 'DoubleLongLeftRightArrow;': '\u27fa', - 'DoubleLongRightArrow;': '\u27f9', - 'DoubleRightArrow;': '\u21d2', - 'DoubleRightTee;': '\u22a8', - 'DoubleUpArrow;': '\u21d1', - 'DoubleUpDownArrow;': '\u21d5', - 'DoubleVerticalBar;': '\u2225', - 'DownArrow;': '\u2193', - 'Downarrow;': '\u21d3', - 'downarrow;': '\u2193', - 'DownArrowBar;': '\u2913', - 'DownArrowUpArrow;': '\u21f5', - 'DownBreve;': '\u0311', - 'downdownarrows;': '\u21ca', - 'downharpoonleft;': '\u21c3', - 'downharpoonright;': '\u21c2', - 'DownLeftRightVector;': '\u2950', - 'DownLeftTeeVector;': '\u295e', - 'DownLeftVector;': '\u21bd', - 'DownLeftVectorBar;': '\u2956', - 'DownRightTeeVector;': '\u295f', - 'DownRightVector;': '\u21c1', - 'DownRightVectorBar;': '\u2957', - 'DownTee;': '\u22a4', - 'DownTeeArrow;': '\u21a7', - 'drbkarow;': '\u2910', - 'drcorn;': '\u231f', - 'drcrop;': '\u230c', - 'Dscr;': '\U0001d49f', - 'dscr;': '\U0001d4b9', - 'DScy;': '\u0405', - 'dscy;': '\u0455', - 'dsol;': '\u29f6', - 'Dstrok;': '\u0110', - 'dstrok;': '\u0111', - 'dtdot;': '\u22f1', - 'dtri;': '\u25bf', - 'dtrif;': '\u25be', - 'duarr;': '\u21f5', - 'duhar;': '\u296f', - 'dwangle;': '\u29a6', - 'DZcy;': '\u040f', - 'dzcy;': '\u045f', - 'dzigrarr;': '\u27ff', - 'Eacute': '\xc9', - 'eacute': '\xe9', - 'Eacute;': '\xc9', - 'eacute;': '\xe9', - 'easter;': '\u2a6e', - 'Ecaron;': '\u011a', - 'ecaron;': '\u011b', - 'ecir;': '\u2256', - 'Ecirc': '\xca', - 'ecirc': '\xea', - 'Ecirc;': '\xca', - 'ecirc;': '\xea', - 'ecolon;': '\u2255', - 'Ecy;': '\u042d', - 'ecy;': '\u044d', - 'eDDot;': '\u2a77', - 'Edot;': '\u0116', - 'eDot;': '\u2251', - 'edot;': '\u0117', - 'ee;': '\u2147', - 'efDot;': '\u2252', - 'Efr;': '\U0001d508', - 'efr;': '\U0001d522', - 'eg;': '\u2a9a', - 'Egrave': '\xc8', - 'egrave': '\xe8', - 'Egrave;': '\xc8', - 'egrave;': '\xe8', - 'egs;': '\u2a96', - 'egsdot;': '\u2a98', - 'el;': '\u2a99', - 'Element;': '\u2208', - 'elinters;': '\u23e7', - 'ell;': '\u2113', - 'els;': '\u2a95', - 'elsdot;': '\u2a97', - 'Emacr;': '\u0112', - 'emacr;': '\u0113', - 'empty;': '\u2205', - 'emptyset;': '\u2205', - 'EmptySmallSquare;': '\u25fb', - 'emptyv;': '\u2205', - 'EmptyVerySmallSquare;': '\u25ab', - 'emsp13;': '\u2004', - 'emsp14;': '\u2005', - 'emsp;': '\u2003', - 'ENG;': '\u014a', - 'eng;': '\u014b', - 'ensp;': '\u2002', - 'Eogon;': '\u0118', - 'eogon;': '\u0119', - 'Eopf;': '\U0001d53c', - 'eopf;': '\U0001d556', - 'epar;': '\u22d5', - 'eparsl;': '\u29e3', - 'eplus;': '\u2a71', - 'epsi;': '\u03b5', - 'Epsilon;': '\u0395', - 'epsilon;': '\u03b5', - 'epsiv;': '\u03f5', - 'eqcirc;': '\u2256', - 'eqcolon;': '\u2255', - 'eqsim;': '\u2242', - 'eqslantgtr;': '\u2a96', - 'eqslantless;': '\u2a95', - 'Equal;': '\u2a75', - 'equals;': '=', - 'EqualTilde;': '\u2242', - 'equest;': '\u225f', - 'Equilibrium;': '\u21cc', - 'equiv;': '\u2261', - 'equivDD;': '\u2a78', - 'eqvparsl;': '\u29e5', - 'erarr;': '\u2971', - 'erDot;': '\u2253', - 'Escr;': '\u2130', - 'escr;': '\u212f', - 'esdot;': '\u2250', - 'Esim;': '\u2a73', - 'esim;': '\u2242', - 'Eta;': '\u0397', - 'eta;': '\u03b7', - 'ETH': '\xd0', - 'eth': '\xf0', - 'ETH;': '\xd0', - 'eth;': '\xf0', - 'Euml': '\xcb', - 'euml': '\xeb', - 'Euml;': '\xcb', - 'euml;': '\xeb', - 'euro;': '\u20ac', - 'excl;': '!', - 'exist;': '\u2203', - 'Exists;': '\u2203', - 'expectation;': '\u2130', - 'ExponentialE;': '\u2147', - 'exponentiale;': '\u2147', - 'fallingdotseq;': '\u2252', - 'Fcy;': '\u0424', - 'fcy;': '\u0444', - 'female;': '\u2640', - 'ffilig;': '\ufb03', - 'fflig;': '\ufb00', - 'ffllig;': '\ufb04', - 'Ffr;': '\U0001d509', - 'ffr;': '\U0001d523', - 'filig;': '\ufb01', - 'FilledSmallSquare;': '\u25fc', - 'FilledVerySmallSquare;': '\u25aa', - 'fjlig;': 'fj', - 'flat;': '\u266d', - 'fllig;': '\ufb02', - 'fltns;': '\u25b1', - 'fnof;': '\u0192', - 'Fopf;': '\U0001d53d', - 'fopf;': '\U0001d557', - 'ForAll;': '\u2200', - 'forall;': '\u2200', - 'fork;': '\u22d4', - 'forkv;': '\u2ad9', - 'Fouriertrf;': '\u2131', - 'fpartint;': '\u2a0d', - 'frac12': '\xbd', - 'frac12;': '\xbd', - 'frac13;': '\u2153', - 'frac14': '\xbc', - 'frac14;': '\xbc', - 'frac15;': '\u2155', - 'frac16;': '\u2159', - 'frac18;': '\u215b', - 'frac23;': '\u2154', - 'frac25;': '\u2156', - 'frac34': '\xbe', - 'frac34;': '\xbe', - 'frac35;': '\u2157', - 'frac38;': '\u215c', - 'frac45;': '\u2158', - 'frac56;': '\u215a', - 'frac58;': '\u215d', - 'frac78;': '\u215e', - 'frasl;': '\u2044', - 'frown;': '\u2322', - 'Fscr;': '\u2131', - 'fscr;': '\U0001d4bb', - 'gacute;': '\u01f5', - 'Gamma;': '\u0393', - 'gamma;': '\u03b3', - 'Gammad;': '\u03dc', - 'gammad;': '\u03dd', - 'gap;': '\u2a86', - 'Gbreve;': '\u011e', - 'gbreve;': '\u011f', - 'Gcedil;': '\u0122', - 'Gcirc;': '\u011c', - 'gcirc;': '\u011d', - 'Gcy;': '\u0413', - 'gcy;': '\u0433', - 'Gdot;': '\u0120', - 'gdot;': '\u0121', - 'gE;': '\u2267', - 'ge;': '\u2265', - 'gEl;': '\u2a8c', - 'gel;': '\u22db', - 'geq;': '\u2265', - 'geqq;': '\u2267', - 'geqslant;': '\u2a7e', - 'ges;': '\u2a7e', - 'gescc;': '\u2aa9', - 'gesdot;': '\u2a80', - 'gesdoto;': '\u2a82', - 'gesdotol;': '\u2a84', - 'gesl;': '\u22db\ufe00', - 'gesles;': '\u2a94', - 'Gfr;': '\U0001d50a', - 'gfr;': '\U0001d524', - 'Gg;': '\u22d9', - 'gg;': '\u226b', - 'ggg;': '\u22d9', - 'gimel;': '\u2137', - 'GJcy;': '\u0403', - 'gjcy;': '\u0453', - 'gl;': '\u2277', - 'gla;': '\u2aa5', - 'glE;': '\u2a92', - 'glj;': '\u2aa4', - 'gnap;': '\u2a8a', - 'gnapprox;': '\u2a8a', - 'gnE;': '\u2269', - 'gne;': '\u2a88', - 'gneq;': '\u2a88', - 'gneqq;': '\u2269', - 'gnsim;': '\u22e7', - 'Gopf;': '\U0001d53e', - 'gopf;': '\U0001d558', - 'grave;': '`', - 'GreaterEqual;': '\u2265', - 'GreaterEqualLess;': '\u22db', - 'GreaterFullEqual;': '\u2267', - 'GreaterGreater;': '\u2aa2', - 'GreaterLess;': '\u2277', - 'GreaterSlantEqual;': '\u2a7e', - 'GreaterTilde;': '\u2273', - 'Gscr;': '\U0001d4a2', - 'gscr;': '\u210a', - 'gsim;': '\u2273', - 'gsime;': '\u2a8e', - 'gsiml;': '\u2a90', - 'GT': '>', - 'gt': '>', - 'GT;': '>', - 'Gt;': '\u226b', - 'gt;': '>', - 'gtcc;': '\u2aa7', - 'gtcir;': '\u2a7a', - 'gtdot;': '\u22d7', - 'gtlPar;': '\u2995', - 'gtquest;': '\u2a7c', - 'gtrapprox;': '\u2a86', - 'gtrarr;': '\u2978', - 'gtrdot;': '\u22d7', - 'gtreqless;': '\u22db', - 'gtreqqless;': '\u2a8c', - 'gtrless;': '\u2277', - 'gtrsim;': '\u2273', - 'gvertneqq;': '\u2269\ufe00', - 'gvnE;': '\u2269\ufe00', - 'Hacek;': '\u02c7', - 'hairsp;': '\u200a', - 'half;': '\xbd', - 'hamilt;': '\u210b', - 'HARDcy;': '\u042a', - 'hardcy;': '\u044a', - 'hArr;': '\u21d4', - 'harr;': '\u2194', - 'harrcir;': '\u2948', - 'harrw;': '\u21ad', - 'Hat;': '^', - 'hbar;': '\u210f', - 'Hcirc;': '\u0124', - 'hcirc;': '\u0125', - 'hearts;': '\u2665', - 'heartsuit;': '\u2665', - 'hellip;': '\u2026', - 'hercon;': '\u22b9', - 'Hfr;': '\u210c', - 'hfr;': '\U0001d525', - 'HilbertSpace;': '\u210b', - 'hksearow;': '\u2925', - 'hkswarow;': '\u2926', - 'hoarr;': '\u21ff', - 'homtht;': '\u223b', - 'hookleftarrow;': '\u21a9', - 'hookrightarrow;': '\u21aa', - 'Hopf;': '\u210d', - 'hopf;': '\U0001d559', - 'horbar;': '\u2015', - 'HorizontalLine;': '\u2500', - 'Hscr;': '\u210b', - 'hscr;': '\U0001d4bd', - 'hslash;': '\u210f', - 'Hstrok;': '\u0126', - 'hstrok;': '\u0127', - 'HumpDownHump;': '\u224e', - 'HumpEqual;': '\u224f', - 'hybull;': '\u2043', - 'hyphen;': '\u2010', - 'Iacute': '\xcd', - 'iacute': '\xed', - 'Iacute;': '\xcd', - 'iacute;': '\xed', - 'ic;': '\u2063', - 'Icirc': '\xce', - 'icirc': '\xee', - 'Icirc;': '\xce', - 'icirc;': '\xee', - 'Icy;': '\u0418', - 'icy;': '\u0438', - 'Idot;': '\u0130', - 'IEcy;': '\u0415', - 'iecy;': '\u0435', - 'iexcl': '\xa1', - 'iexcl;': '\xa1', - 'iff;': '\u21d4', - 'Ifr;': '\u2111', - 'ifr;': '\U0001d526', - 'Igrave': '\xcc', - 'igrave': '\xec', - 'Igrave;': '\xcc', - 'igrave;': '\xec', - 'ii;': '\u2148', - 'iiiint;': '\u2a0c', - 'iiint;': '\u222d', - 'iinfin;': '\u29dc', - 'iiota;': '\u2129', - 'IJlig;': '\u0132', - 'ijlig;': '\u0133', - 'Im;': '\u2111', - 'Imacr;': '\u012a', - 'imacr;': '\u012b', - 'image;': '\u2111', - 'ImaginaryI;': '\u2148', - 'imagline;': '\u2110', - 'imagpart;': '\u2111', - 'imath;': '\u0131', - 'imof;': '\u22b7', - 'imped;': '\u01b5', - 'Implies;': '\u21d2', - 'in;': '\u2208', - 'incare;': '\u2105', - 'infin;': '\u221e', - 'infintie;': '\u29dd', - 'inodot;': '\u0131', - 'Int;': '\u222c', - 'int;': '\u222b', - 'intcal;': '\u22ba', - 'integers;': '\u2124', - 'Integral;': '\u222b', - 'intercal;': '\u22ba', - 'Intersection;': '\u22c2', - 'intlarhk;': '\u2a17', - 'intprod;': '\u2a3c', - 'InvisibleComma;': '\u2063', - 'InvisibleTimes;': '\u2062', - 'IOcy;': '\u0401', - 'iocy;': '\u0451', - 'Iogon;': '\u012e', - 'iogon;': '\u012f', - 'Iopf;': '\U0001d540', - 'iopf;': '\U0001d55a', - 'Iota;': '\u0399', - 'iota;': '\u03b9', - 'iprod;': '\u2a3c', - 'iquest': '\xbf', - 'iquest;': '\xbf', - 'Iscr;': '\u2110', - 'iscr;': '\U0001d4be', - 'isin;': '\u2208', - 'isindot;': '\u22f5', - 'isinE;': '\u22f9', - 'isins;': '\u22f4', - 'isinsv;': '\u22f3', - 'isinv;': '\u2208', - 'it;': '\u2062', - 'Itilde;': '\u0128', - 'itilde;': '\u0129', - 'Iukcy;': '\u0406', - 'iukcy;': '\u0456', - 'Iuml': '\xcf', - 'iuml': '\xef', - 'Iuml;': '\xcf', - 'iuml;': '\xef', - 'Jcirc;': '\u0134', - 'jcirc;': '\u0135', - 'Jcy;': '\u0419', - 'jcy;': '\u0439', - 'Jfr;': '\U0001d50d', - 'jfr;': '\U0001d527', - 'jmath;': '\u0237', - 'Jopf;': '\U0001d541', - 'jopf;': '\U0001d55b', - 'Jscr;': '\U0001d4a5', - 'jscr;': '\U0001d4bf', - 'Jsercy;': '\u0408', - 'jsercy;': '\u0458', - 'Jukcy;': '\u0404', - 'jukcy;': '\u0454', - 'Kappa;': '\u039a', - 'kappa;': '\u03ba', - 'kappav;': '\u03f0', - 'Kcedil;': '\u0136', - 'kcedil;': '\u0137', - 'Kcy;': '\u041a', - 'kcy;': '\u043a', - 'Kfr;': '\U0001d50e', - 'kfr;': '\U0001d528', - 'kgreen;': '\u0138', - 'KHcy;': '\u0425', - 'khcy;': '\u0445', - 'KJcy;': '\u040c', - 'kjcy;': '\u045c', - 'Kopf;': '\U0001d542', - 'kopf;': '\U0001d55c', - 'Kscr;': '\U0001d4a6', - 'kscr;': '\U0001d4c0', - 'lAarr;': '\u21da', - 'Lacute;': '\u0139', - 'lacute;': '\u013a', - 'laemptyv;': '\u29b4', - 'lagran;': '\u2112', - 'Lambda;': '\u039b', - 'lambda;': '\u03bb', - 'Lang;': '\u27ea', - 'lang;': '\u27e8', - 'langd;': '\u2991', - 'langle;': '\u27e8', - 'lap;': '\u2a85', - 'Laplacetrf;': '\u2112', - 'laquo': '\xab', - 'laquo;': '\xab', - 'Larr;': '\u219e', - 'lArr;': '\u21d0', - 'larr;': '\u2190', - 'larrb;': '\u21e4', - 'larrbfs;': '\u291f', - 'larrfs;': '\u291d', - 'larrhk;': '\u21a9', - 'larrlp;': '\u21ab', - 'larrpl;': '\u2939', - 'larrsim;': '\u2973', - 'larrtl;': '\u21a2', - 'lat;': '\u2aab', - 'lAtail;': '\u291b', - 'latail;': '\u2919', - 'late;': '\u2aad', - 'lates;': '\u2aad\ufe00', - 'lBarr;': '\u290e', - 'lbarr;': '\u290c', - 'lbbrk;': '\u2772', - 'lbrace;': '{', - 'lbrack;': '[', - 'lbrke;': '\u298b', - 'lbrksld;': '\u298f', - 'lbrkslu;': '\u298d', - 'Lcaron;': '\u013d', - 'lcaron;': '\u013e', - 'Lcedil;': '\u013b', - 'lcedil;': '\u013c', - 'lceil;': '\u2308', - 'lcub;': '{', - 'Lcy;': '\u041b', - 'lcy;': '\u043b', - 'ldca;': '\u2936', - 'ldquo;': '\u201c', - 'ldquor;': '\u201e', - 'ldrdhar;': '\u2967', - 'ldrushar;': '\u294b', - 'ldsh;': '\u21b2', - 'lE;': '\u2266', - 'le;': '\u2264', - 'LeftAngleBracket;': '\u27e8', - 'LeftArrow;': '\u2190', - 'Leftarrow;': '\u21d0', - 'leftarrow;': '\u2190', - 'LeftArrowBar;': '\u21e4', - 'LeftArrowRightArrow;': '\u21c6', - 'leftarrowtail;': '\u21a2', - 'LeftCeiling;': '\u2308', - 'LeftDoubleBracket;': '\u27e6', - 'LeftDownTeeVector;': '\u2961', - 'LeftDownVector;': '\u21c3', - 'LeftDownVectorBar;': '\u2959', - 'LeftFloor;': '\u230a', - 'leftharpoondown;': '\u21bd', - 'leftharpoonup;': '\u21bc', - 'leftleftarrows;': '\u21c7', - 'LeftRightArrow;': '\u2194', - 'Leftrightarrow;': '\u21d4', - 'leftrightarrow;': '\u2194', - 'leftrightarrows;': '\u21c6', - 'leftrightharpoons;': '\u21cb', - 'leftrightsquigarrow;': '\u21ad', - 'LeftRightVector;': '\u294e', - 'LeftTee;': '\u22a3', - 'LeftTeeArrow;': '\u21a4', - 'LeftTeeVector;': '\u295a', - 'leftthreetimes;': '\u22cb', - 'LeftTriangle;': '\u22b2', - 'LeftTriangleBar;': '\u29cf', - 'LeftTriangleEqual;': '\u22b4', - 'LeftUpDownVector;': '\u2951', - 'LeftUpTeeVector;': '\u2960', - 'LeftUpVector;': '\u21bf', - 'LeftUpVectorBar;': '\u2958', - 'LeftVector;': '\u21bc', - 'LeftVectorBar;': '\u2952', - 'lEg;': '\u2a8b', - 'leg;': '\u22da', - 'leq;': '\u2264', - 'leqq;': '\u2266', - 'leqslant;': '\u2a7d', - 'les;': '\u2a7d', - 'lescc;': '\u2aa8', - 'lesdot;': '\u2a7f', - 'lesdoto;': '\u2a81', - 'lesdotor;': '\u2a83', - 'lesg;': '\u22da\ufe00', - 'lesges;': '\u2a93', - 'lessapprox;': '\u2a85', - 'lessdot;': '\u22d6', - 'lesseqgtr;': '\u22da', - 'lesseqqgtr;': '\u2a8b', - 'LessEqualGreater;': '\u22da', - 'LessFullEqual;': '\u2266', - 'LessGreater;': '\u2276', - 'lessgtr;': '\u2276', - 'LessLess;': '\u2aa1', - 'lesssim;': '\u2272', - 'LessSlantEqual;': '\u2a7d', - 'LessTilde;': '\u2272', - 'lfisht;': '\u297c', - 'lfloor;': '\u230a', - 'Lfr;': '\U0001d50f', - 'lfr;': '\U0001d529', - 'lg;': '\u2276', - 'lgE;': '\u2a91', - 'lHar;': '\u2962', - 'lhard;': '\u21bd', - 'lharu;': '\u21bc', - 'lharul;': '\u296a', - 'lhblk;': '\u2584', - 'LJcy;': '\u0409', - 'ljcy;': '\u0459', - 'Ll;': '\u22d8', - 'll;': '\u226a', - 'llarr;': '\u21c7', - 'llcorner;': '\u231e', - 'Lleftarrow;': '\u21da', - 'llhard;': '\u296b', - 'lltri;': '\u25fa', - 'Lmidot;': '\u013f', - 'lmidot;': '\u0140', - 'lmoust;': '\u23b0', - 'lmoustache;': '\u23b0', - 'lnap;': '\u2a89', - 'lnapprox;': '\u2a89', - 'lnE;': '\u2268', - 'lne;': '\u2a87', - 'lneq;': '\u2a87', - 'lneqq;': '\u2268', - 'lnsim;': '\u22e6', - 'loang;': '\u27ec', - 'loarr;': '\u21fd', - 'lobrk;': '\u27e6', - 'LongLeftArrow;': '\u27f5', - 'Longleftarrow;': '\u27f8', - 'longleftarrow;': '\u27f5', - 'LongLeftRightArrow;': '\u27f7', - 'Longleftrightarrow;': '\u27fa', - 'longleftrightarrow;': '\u27f7', - 'longmapsto;': '\u27fc', - 'LongRightArrow;': '\u27f6', - 'Longrightarrow;': '\u27f9', - 'longrightarrow;': '\u27f6', - 'looparrowleft;': '\u21ab', - 'looparrowright;': '\u21ac', - 'lopar;': '\u2985', - 'Lopf;': '\U0001d543', - 'lopf;': '\U0001d55d', - 'loplus;': '\u2a2d', - 'lotimes;': '\u2a34', - 'lowast;': '\u2217', - 'lowbar;': '_', - 'LowerLeftArrow;': '\u2199', - 'LowerRightArrow;': '\u2198', - 'loz;': '\u25ca', - 'lozenge;': '\u25ca', - 'lozf;': '\u29eb', - 'lpar;': '(', - 'lparlt;': '\u2993', - 'lrarr;': '\u21c6', - 'lrcorner;': '\u231f', - 'lrhar;': '\u21cb', - 'lrhard;': '\u296d', - 'lrm;': '\u200e', - 'lrtri;': '\u22bf', - 'lsaquo;': '\u2039', - 'Lscr;': '\u2112', - 'lscr;': '\U0001d4c1', - 'Lsh;': '\u21b0', - 'lsh;': '\u21b0', - 'lsim;': '\u2272', - 'lsime;': '\u2a8d', - 'lsimg;': '\u2a8f', - 'lsqb;': '[', - 'lsquo;': '\u2018', - 'lsquor;': '\u201a', - 'Lstrok;': '\u0141', - 'lstrok;': '\u0142', - 'LT': '<', - 'lt': '<', - 'LT;': '<', - 'Lt;': '\u226a', - 'lt;': '<', - 'ltcc;': '\u2aa6', - 'ltcir;': '\u2a79', - 'ltdot;': '\u22d6', - 'lthree;': '\u22cb', - 'ltimes;': '\u22c9', - 'ltlarr;': '\u2976', - 'ltquest;': '\u2a7b', - 'ltri;': '\u25c3', - 'ltrie;': '\u22b4', - 'ltrif;': '\u25c2', - 'ltrPar;': '\u2996', - 'lurdshar;': '\u294a', - 'luruhar;': '\u2966', - 'lvertneqq;': '\u2268\ufe00', - 'lvnE;': '\u2268\ufe00', - 'macr': '\xaf', - 'macr;': '\xaf', - 'male;': '\u2642', - 'malt;': '\u2720', - 'maltese;': '\u2720', - 'Map;': '\u2905', - 'map;': '\u21a6', - 'mapsto;': '\u21a6', - 'mapstodown;': '\u21a7', - 'mapstoleft;': '\u21a4', - 'mapstoup;': '\u21a5', - 'marker;': '\u25ae', - 'mcomma;': '\u2a29', - 'Mcy;': '\u041c', - 'mcy;': '\u043c', - 'mdash;': '\u2014', - 'mDDot;': '\u223a', - 'measuredangle;': '\u2221', - 'MediumSpace;': '\u205f', - 'Mellintrf;': '\u2133', - 'Mfr;': '\U0001d510', - 'mfr;': '\U0001d52a', - 'mho;': '\u2127', - 'micro': '\xb5', - 'micro;': '\xb5', - 'mid;': '\u2223', - 'midast;': '*', - 'midcir;': '\u2af0', - 'middot': '\xb7', - 'middot;': '\xb7', - 'minus;': '\u2212', - 'minusb;': '\u229f', - 'minusd;': '\u2238', - 'minusdu;': '\u2a2a', - 'MinusPlus;': '\u2213', - 'mlcp;': '\u2adb', - 'mldr;': '\u2026', - 'mnplus;': '\u2213', - 'models;': '\u22a7', - 'Mopf;': '\U0001d544', - 'mopf;': '\U0001d55e', - 'mp;': '\u2213', - 'Mscr;': '\u2133', - 'mscr;': '\U0001d4c2', - 'mstpos;': '\u223e', - 'Mu;': '\u039c', - 'mu;': '\u03bc', - 'multimap;': '\u22b8', - 'mumap;': '\u22b8', - 'nabla;': '\u2207', - 'Nacute;': '\u0143', - 'nacute;': '\u0144', - 'nang;': '\u2220\u20d2', - 'nap;': '\u2249', - 'napE;': '\u2a70\u0338', - 'napid;': '\u224b\u0338', - 'napos;': '\u0149', - 'napprox;': '\u2249', - 'natur;': '\u266e', - 'natural;': '\u266e', - 'naturals;': '\u2115', - 'nbsp': '\xa0', - 'nbsp;': '\xa0', - 'nbump;': '\u224e\u0338', - 'nbumpe;': '\u224f\u0338', - 'ncap;': '\u2a43', - 'Ncaron;': '\u0147', - 'ncaron;': '\u0148', - 'Ncedil;': '\u0145', - 'ncedil;': '\u0146', - 'ncong;': '\u2247', - 'ncongdot;': '\u2a6d\u0338', - 'ncup;': '\u2a42', - 'Ncy;': '\u041d', - 'ncy;': '\u043d', - 'ndash;': '\u2013', - 'ne;': '\u2260', - 'nearhk;': '\u2924', - 'neArr;': '\u21d7', - 'nearr;': '\u2197', - 'nearrow;': '\u2197', - 'nedot;': '\u2250\u0338', - 'NegativeMediumSpace;': '\u200b', - 'NegativeThickSpace;': '\u200b', - 'NegativeThinSpace;': '\u200b', - 'NegativeVeryThinSpace;': '\u200b', - 'nequiv;': '\u2262', - 'nesear;': '\u2928', - 'nesim;': '\u2242\u0338', - 'NestedGreaterGreater;': '\u226b', - 'NestedLessLess;': '\u226a', - 'NewLine;': '\n', - 'nexist;': '\u2204', - 'nexists;': '\u2204', - 'Nfr;': '\U0001d511', - 'nfr;': '\U0001d52b', - 'ngE;': '\u2267\u0338', - 'nge;': '\u2271', - 'ngeq;': '\u2271', - 'ngeqq;': '\u2267\u0338', - 'ngeqslant;': '\u2a7e\u0338', - 'nges;': '\u2a7e\u0338', - 'nGg;': '\u22d9\u0338', - 'ngsim;': '\u2275', - 'nGt;': '\u226b\u20d2', - 'ngt;': '\u226f', - 'ngtr;': '\u226f', - 'nGtv;': '\u226b\u0338', - 'nhArr;': '\u21ce', - 'nharr;': '\u21ae', - 'nhpar;': '\u2af2', - 'ni;': '\u220b', - 'nis;': '\u22fc', - 'nisd;': '\u22fa', - 'niv;': '\u220b', - 'NJcy;': '\u040a', - 'njcy;': '\u045a', - 'nlArr;': '\u21cd', - 'nlarr;': '\u219a', - 'nldr;': '\u2025', - 'nlE;': '\u2266\u0338', - 'nle;': '\u2270', - 'nLeftarrow;': '\u21cd', - 'nleftarrow;': '\u219a', - 'nLeftrightarrow;': '\u21ce', - 'nleftrightarrow;': '\u21ae', - 'nleq;': '\u2270', - 'nleqq;': '\u2266\u0338', - 'nleqslant;': '\u2a7d\u0338', - 'nles;': '\u2a7d\u0338', - 'nless;': '\u226e', - 'nLl;': '\u22d8\u0338', - 'nlsim;': '\u2274', - 'nLt;': '\u226a\u20d2', - 'nlt;': '\u226e', - 'nltri;': '\u22ea', - 'nltrie;': '\u22ec', - 'nLtv;': '\u226a\u0338', - 'nmid;': '\u2224', - 'NoBreak;': '\u2060', - 'NonBreakingSpace;': '\xa0', - 'Nopf;': '\u2115', - 'nopf;': '\U0001d55f', - 'not': '\xac', - 'Not;': '\u2aec', - 'not;': '\xac', - 'NotCongruent;': '\u2262', - 'NotCupCap;': '\u226d', - 'NotDoubleVerticalBar;': '\u2226', - 'NotElement;': '\u2209', - 'NotEqual;': '\u2260', - 'NotEqualTilde;': '\u2242\u0338', - 'NotExists;': '\u2204', - 'NotGreater;': '\u226f', - 'NotGreaterEqual;': '\u2271', - 'NotGreaterFullEqual;': '\u2267\u0338', - 'NotGreaterGreater;': '\u226b\u0338', - 'NotGreaterLess;': '\u2279', - 'NotGreaterSlantEqual;': '\u2a7e\u0338', - 'NotGreaterTilde;': '\u2275', - 'NotHumpDownHump;': '\u224e\u0338', - 'NotHumpEqual;': '\u224f\u0338', - 'notin;': '\u2209', - 'notindot;': '\u22f5\u0338', - 'notinE;': '\u22f9\u0338', - 'notinva;': '\u2209', - 'notinvb;': '\u22f7', - 'notinvc;': '\u22f6', - 'NotLeftTriangle;': '\u22ea', - 'NotLeftTriangleBar;': '\u29cf\u0338', - 'NotLeftTriangleEqual;': '\u22ec', - 'NotLess;': '\u226e', - 'NotLessEqual;': '\u2270', - 'NotLessGreater;': '\u2278', - 'NotLessLess;': '\u226a\u0338', - 'NotLessSlantEqual;': '\u2a7d\u0338', - 'NotLessTilde;': '\u2274', - 'NotNestedGreaterGreater;': '\u2aa2\u0338', - 'NotNestedLessLess;': '\u2aa1\u0338', - 'notni;': '\u220c', - 'notniva;': '\u220c', - 'notnivb;': '\u22fe', - 'notnivc;': '\u22fd', - 'NotPrecedes;': '\u2280', - 'NotPrecedesEqual;': '\u2aaf\u0338', - 'NotPrecedesSlantEqual;': '\u22e0', - 'NotReverseElement;': '\u220c', - 'NotRightTriangle;': '\u22eb', - 'NotRightTriangleBar;': '\u29d0\u0338', - 'NotRightTriangleEqual;': '\u22ed', - 'NotSquareSubset;': '\u228f\u0338', - 'NotSquareSubsetEqual;': '\u22e2', - 'NotSquareSuperset;': '\u2290\u0338', - 'NotSquareSupersetEqual;': '\u22e3', - 'NotSubset;': '\u2282\u20d2', - 'NotSubsetEqual;': '\u2288', - 'NotSucceeds;': '\u2281', - 'NotSucceedsEqual;': '\u2ab0\u0338', - 'NotSucceedsSlantEqual;': '\u22e1', - 'NotSucceedsTilde;': '\u227f\u0338', - 'NotSuperset;': '\u2283\u20d2', - 'NotSupersetEqual;': '\u2289', - 'NotTilde;': '\u2241', - 'NotTildeEqual;': '\u2244', - 'NotTildeFullEqual;': '\u2247', - 'NotTildeTilde;': '\u2249', - 'NotVerticalBar;': '\u2224', - 'npar;': '\u2226', - 'nparallel;': '\u2226', - 'nparsl;': '\u2afd\u20e5', - 'npart;': '\u2202\u0338', - 'npolint;': '\u2a14', - 'npr;': '\u2280', - 'nprcue;': '\u22e0', - 'npre;': '\u2aaf\u0338', - 'nprec;': '\u2280', - 'npreceq;': '\u2aaf\u0338', - 'nrArr;': '\u21cf', - 'nrarr;': '\u219b', - 'nrarrc;': '\u2933\u0338', - 'nrarrw;': '\u219d\u0338', - 'nRightarrow;': '\u21cf', - 'nrightarrow;': '\u219b', - 'nrtri;': '\u22eb', - 'nrtrie;': '\u22ed', - 'nsc;': '\u2281', - 'nsccue;': '\u22e1', - 'nsce;': '\u2ab0\u0338', - 'Nscr;': '\U0001d4a9', - 'nscr;': '\U0001d4c3', - 'nshortmid;': '\u2224', - 'nshortparallel;': '\u2226', - 'nsim;': '\u2241', - 'nsime;': '\u2244', - 'nsimeq;': '\u2244', - 'nsmid;': '\u2224', - 'nspar;': '\u2226', - 'nsqsube;': '\u22e2', - 'nsqsupe;': '\u22e3', - 'nsub;': '\u2284', - 'nsubE;': '\u2ac5\u0338', - 'nsube;': '\u2288', - 'nsubset;': '\u2282\u20d2', - 'nsubseteq;': '\u2288', - 'nsubseteqq;': '\u2ac5\u0338', - 'nsucc;': '\u2281', - 'nsucceq;': '\u2ab0\u0338', - 'nsup;': '\u2285', - 'nsupE;': '\u2ac6\u0338', - 'nsupe;': '\u2289', - 'nsupset;': '\u2283\u20d2', - 'nsupseteq;': '\u2289', - 'nsupseteqq;': '\u2ac6\u0338', - 'ntgl;': '\u2279', - 'Ntilde': '\xd1', - 'ntilde': '\xf1', - 'Ntilde;': '\xd1', - 'ntilde;': '\xf1', - 'ntlg;': '\u2278', - 'ntriangleleft;': '\u22ea', - 'ntrianglelefteq;': '\u22ec', - 'ntriangleright;': '\u22eb', - 'ntrianglerighteq;': '\u22ed', - 'Nu;': '\u039d', - 'nu;': '\u03bd', - 'num;': '#', - 'numero;': '\u2116', - 'numsp;': '\u2007', - 'nvap;': '\u224d\u20d2', - 'nVDash;': '\u22af', - 'nVdash;': '\u22ae', - 'nvDash;': '\u22ad', - 'nvdash;': '\u22ac', - 'nvge;': '\u2265\u20d2', - 'nvgt;': '>\u20d2', - 'nvHarr;': '\u2904', - 'nvinfin;': '\u29de', - 'nvlArr;': '\u2902', - 'nvle;': '\u2264\u20d2', - 'nvlt;': '<\u20d2', - 'nvltrie;': '\u22b4\u20d2', - 'nvrArr;': '\u2903', - 'nvrtrie;': '\u22b5\u20d2', - 'nvsim;': '\u223c\u20d2', - 'nwarhk;': '\u2923', - 'nwArr;': '\u21d6', - 'nwarr;': '\u2196', - 'nwarrow;': '\u2196', - 'nwnear;': '\u2927', - 'Oacute': '\xd3', - 'oacute': '\xf3', - 'Oacute;': '\xd3', - 'oacute;': '\xf3', - 'oast;': '\u229b', - 'ocir;': '\u229a', - 'Ocirc': '\xd4', - 'ocirc': '\xf4', - 'Ocirc;': '\xd4', - 'ocirc;': '\xf4', - 'Ocy;': '\u041e', - 'ocy;': '\u043e', - 'odash;': '\u229d', - 'Odblac;': '\u0150', - 'odblac;': '\u0151', - 'odiv;': '\u2a38', - 'odot;': '\u2299', - 'odsold;': '\u29bc', - 'OElig;': '\u0152', - 'oelig;': '\u0153', - 'ofcir;': '\u29bf', - 'Ofr;': '\U0001d512', - 'ofr;': '\U0001d52c', - 'ogon;': '\u02db', - 'Ograve': '\xd2', - 'ograve': '\xf2', - 'Ograve;': '\xd2', - 'ograve;': '\xf2', - 'ogt;': '\u29c1', - 'ohbar;': '\u29b5', - 'ohm;': '\u03a9', - 'oint;': '\u222e', - 'olarr;': '\u21ba', - 'olcir;': '\u29be', - 'olcross;': '\u29bb', - 'oline;': '\u203e', - 'olt;': '\u29c0', - 'Omacr;': '\u014c', - 'omacr;': '\u014d', - 'Omega;': '\u03a9', - 'omega;': '\u03c9', - 'Omicron;': '\u039f', - 'omicron;': '\u03bf', - 'omid;': '\u29b6', - 'ominus;': '\u2296', - 'Oopf;': '\U0001d546', - 'oopf;': '\U0001d560', - 'opar;': '\u29b7', - 'OpenCurlyDoubleQuote;': '\u201c', - 'OpenCurlyQuote;': '\u2018', - 'operp;': '\u29b9', - 'oplus;': '\u2295', - 'Or;': '\u2a54', - 'or;': '\u2228', - 'orarr;': '\u21bb', - 'ord;': '\u2a5d', - 'order;': '\u2134', - 'orderof;': '\u2134', - 'ordf': '\xaa', - 'ordf;': '\xaa', - 'ordm': '\xba', - 'ordm;': '\xba', - 'origof;': '\u22b6', - 'oror;': '\u2a56', - 'orslope;': '\u2a57', - 'orv;': '\u2a5b', - 'oS;': '\u24c8', - 'Oscr;': '\U0001d4aa', - 'oscr;': '\u2134', - 'Oslash': '\xd8', - 'oslash': '\xf8', - 'Oslash;': '\xd8', - 'oslash;': '\xf8', - 'osol;': '\u2298', - 'Otilde': '\xd5', - 'otilde': '\xf5', - 'Otilde;': '\xd5', - 'otilde;': '\xf5', - 'Otimes;': '\u2a37', - 'otimes;': '\u2297', - 'otimesas;': '\u2a36', - 'Ouml': '\xd6', - 'ouml': '\xf6', - 'Ouml;': '\xd6', - 'ouml;': '\xf6', - 'ovbar;': '\u233d', - 'OverBar;': '\u203e', - 'OverBrace;': '\u23de', - 'OverBracket;': '\u23b4', - 'OverParenthesis;': '\u23dc', - 'par;': '\u2225', - 'para': '\xb6', - 'para;': '\xb6', - 'parallel;': '\u2225', - 'parsim;': '\u2af3', - 'parsl;': '\u2afd', - 'part;': '\u2202', - 'PartialD;': '\u2202', - 'Pcy;': '\u041f', - 'pcy;': '\u043f', - 'percnt;': '%', - 'period;': '.', - 'permil;': '\u2030', - 'perp;': '\u22a5', - 'pertenk;': '\u2031', - 'Pfr;': '\U0001d513', - 'pfr;': '\U0001d52d', - 'Phi;': '\u03a6', - 'phi;': '\u03c6', - 'phiv;': '\u03d5', - 'phmmat;': '\u2133', - 'phone;': '\u260e', - 'Pi;': '\u03a0', - 'pi;': '\u03c0', - 'pitchfork;': '\u22d4', - 'piv;': '\u03d6', - 'planck;': '\u210f', - 'planckh;': '\u210e', - 'plankv;': '\u210f', - 'plus;': '+', - 'plusacir;': '\u2a23', - 'plusb;': '\u229e', - 'pluscir;': '\u2a22', - 'plusdo;': '\u2214', - 'plusdu;': '\u2a25', - 'pluse;': '\u2a72', - 'PlusMinus;': '\xb1', - 'plusmn': '\xb1', - 'plusmn;': '\xb1', - 'plussim;': '\u2a26', - 'plustwo;': '\u2a27', - 'pm;': '\xb1', - 'Poincareplane;': '\u210c', - 'pointint;': '\u2a15', - 'Popf;': '\u2119', - 'popf;': '\U0001d561', - 'pound': '\xa3', - 'pound;': '\xa3', - 'Pr;': '\u2abb', - 'pr;': '\u227a', - 'prap;': '\u2ab7', - 'prcue;': '\u227c', - 'prE;': '\u2ab3', - 'pre;': '\u2aaf', - 'prec;': '\u227a', - 'precapprox;': '\u2ab7', - 'preccurlyeq;': '\u227c', - 'Precedes;': '\u227a', - 'PrecedesEqual;': '\u2aaf', - 'PrecedesSlantEqual;': '\u227c', - 'PrecedesTilde;': '\u227e', - 'preceq;': '\u2aaf', - 'precnapprox;': '\u2ab9', - 'precneqq;': '\u2ab5', - 'precnsim;': '\u22e8', - 'precsim;': '\u227e', - 'Prime;': '\u2033', - 'prime;': '\u2032', - 'primes;': '\u2119', - 'prnap;': '\u2ab9', - 'prnE;': '\u2ab5', - 'prnsim;': '\u22e8', - 'prod;': '\u220f', - 'Product;': '\u220f', - 'profalar;': '\u232e', - 'profline;': '\u2312', - 'profsurf;': '\u2313', - 'prop;': '\u221d', - 'Proportion;': '\u2237', - 'Proportional;': '\u221d', - 'propto;': '\u221d', - 'prsim;': '\u227e', - 'prurel;': '\u22b0', - 'Pscr;': '\U0001d4ab', - 'pscr;': '\U0001d4c5', - 'Psi;': '\u03a8', - 'psi;': '\u03c8', - 'puncsp;': '\u2008', - 'Qfr;': '\U0001d514', - 'qfr;': '\U0001d52e', - 'qint;': '\u2a0c', - 'Qopf;': '\u211a', - 'qopf;': '\U0001d562', - 'qprime;': '\u2057', - 'Qscr;': '\U0001d4ac', - 'qscr;': '\U0001d4c6', - 'quaternions;': '\u210d', - 'quatint;': '\u2a16', - 'quest;': '?', - 'questeq;': '\u225f', - 'QUOT': '"', - 'quot': '"', - 'QUOT;': '"', - 'quot;': '"', - 'rAarr;': '\u21db', - 'race;': '\u223d\u0331', - 'Racute;': '\u0154', - 'racute;': '\u0155', - 'radic;': '\u221a', - 'raemptyv;': '\u29b3', - 'Rang;': '\u27eb', - 'rang;': '\u27e9', - 'rangd;': '\u2992', - 'range;': '\u29a5', - 'rangle;': '\u27e9', - 'raquo': '\xbb', - 'raquo;': '\xbb', - 'Rarr;': '\u21a0', - 'rArr;': '\u21d2', - 'rarr;': '\u2192', - 'rarrap;': '\u2975', - 'rarrb;': '\u21e5', - 'rarrbfs;': '\u2920', - 'rarrc;': '\u2933', - 'rarrfs;': '\u291e', - 'rarrhk;': '\u21aa', - 'rarrlp;': '\u21ac', - 'rarrpl;': '\u2945', - 'rarrsim;': '\u2974', - 'Rarrtl;': '\u2916', - 'rarrtl;': '\u21a3', - 'rarrw;': '\u219d', - 'rAtail;': '\u291c', - 'ratail;': '\u291a', - 'ratio;': '\u2236', - 'rationals;': '\u211a', - 'RBarr;': '\u2910', - 'rBarr;': '\u290f', - 'rbarr;': '\u290d', - 'rbbrk;': '\u2773', - 'rbrace;': '}', - 'rbrack;': ']', - 'rbrke;': '\u298c', - 'rbrksld;': '\u298e', - 'rbrkslu;': '\u2990', - 'Rcaron;': '\u0158', - 'rcaron;': '\u0159', - 'Rcedil;': '\u0156', - 'rcedil;': '\u0157', - 'rceil;': '\u2309', - 'rcub;': '}', - 'Rcy;': '\u0420', - 'rcy;': '\u0440', - 'rdca;': '\u2937', - 'rdldhar;': '\u2969', - 'rdquo;': '\u201d', - 'rdquor;': '\u201d', - 'rdsh;': '\u21b3', - 'Re;': '\u211c', - 'real;': '\u211c', - 'realine;': '\u211b', - 'realpart;': '\u211c', - 'reals;': '\u211d', - 'rect;': '\u25ad', - 'REG': '\xae', - 'reg': '\xae', - 'REG;': '\xae', - 'reg;': '\xae', - 'ReverseElement;': '\u220b', - 'ReverseEquilibrium;': '\u21cb', - 'ReverseUpEquilibrium;': '\u296f', - 'rfisht;': '\u297d', - 'rfloor;': '\u230b', - 'Rfr;': '\u211c', - 'rfr;': '\U0001d52f', - 'rHar;': '\u2964', - 'rhard;': '\u21c1', - 'rharu;': '\u21c0', - 'rharul;': '\u296c', - 'Rho;': '\u03a1', - 'rho;': '\u03c1', - 'rhov;': '\u03f1', - 'RightAngleBracket;': '\u27e9', - 'RightArrow;': '\u2192', - 'Rightarrow;': '\u21d2', - 'rightarrow;': '\u2192', - 'RightArrowBar;': '\u21e5', - 'RightArrowLeftArrow;': '\u21c4', - 'rightarrowtail;': '\u21a3', - 'RightCeiling;': '\u2309', - 'RightDoubleBracket;': '\u27e7', - 'RightDownTeeVector;': '\u295d', - 'RightDownVector;': '\u21c2', - 'RightDownVectorBar;': '\u2955', - 'RightFloor;': '\u230b', - 'rightharpoondown;': '\u21c1', - 'rightharpoonup;': '\u21c0', - 'rightleftarrows;': '\u21c4', - 'rightleftharpoons;': '\u21cc', - 'rightrightarrows;': '\u21c9', - 'rightsquigarrow;': '\u219d', - 'RightTee;': '\u22a2', - 'RightTeeArrow;': '\u21a6', - 'RightTeeVector;': '\u295b', - 'rightthreetimes;': '\u22cc', - 'RightTriangle;': '\u22b3', - 'RightTriangleBar;': '\u29d0', - 'RightTriangleEqual;': '\u22b5', - 'RightUpDownVector;': '\u294f', - 'RightUpTeeVector;': '\u295c', - 'RightUpVector;': '\u21be', - 'RightUpVectorBar;': '\u2954', - 'RightVector;': '\u21c0', - 'RightVectorBar;': '\u2953', - 'ring;': '\u02da', - 'risingdotseq;': '\u2253', - 'rlarr;': '\u21c4', - 'rlhar;': '\u21cc', - 'rlm;': '\u200f', - 'rmoust;': '\u23b1', - 'rmoustache;': '\u23b1', - 'rnmid;': '\u2aee', - 'roang;': '\u27ed', - 'roarr;': '\u21fe', - 'robrk;': '\u27e7', - 'ropar;': '\u2986', - 'Ropf;': '\u211d', - 'ropf;': '\U0001d563', - 'roplus;': '\u2a2e', - 'rotimes;': '\u2a35', - 'RoundImplies;': '\u2970', - 'rpar;': ')', - 'rpargt;': '\u2994', - 'rppolint;': '\u2a12', - 'rrarr;': '\u21c9', - 'Rrightarrow;': '\u21db', - 'rsaquo;': '\u203a', - 'Rscr;': '\u211b', - 'rscr;': '\U0001d4c7', - 'Rsh;': '\u21b1', - 'rsh;': '\u21b1', - 'rsqb;': ']', - 'rsquo;': '\u2019', - 'rsquor;': '\u2019', - 'rthree;': '\u22cc', - 'rtimes;': '\u22ca', - 'rtri;': '\u25b9', - 'rtrie;': '\u22b5', - 'rtrif;': '\u25b8', - 'rtriltri;': '\u29ce', - 'RuleDelayed;': '\u29f4', - 'ruluhar;': '\u2968', - 'rx;': '\u211e', - 'Sacute;': '\u015a', - 'sacute;': '\u015b', - 'sbquo;': '\u201a', - 'Sc;': '\u2abc', - 'sc;': '\u227b', - 'scap;': '\u2ab8', - 'Scaron;': '\u0160', - 'scaron;': '\u0161', - 'sccue;': '\u227d', - 'scE;': '\u2ab4', - 'sce;': '\u2ab0', - 'Scedil;': '\u015e', - 'scedil;': '\u015f', - 'Scirc;': '\u015c', - 'scirc;': '\u015d', - 'scnap;': '\u2aba', - 'scnE;': '\u2ab6', - 'scnsim;': '\u22e9', - 'scpolint;': '\u2a13', - 'scsim;': '\u227f', - 'Scy;': '\u0421', - 'scy;': '\u0441', - 'sdot;': '\u22c5', - 'sdotb;': '\u22a1', - 'sdote;': '\u2a66', - 'searhk;': '\u2925', - 'seArr;': '\u21d8', - 'searr;': '\u2198', - 'searrow;': '\u2198', - 'sect': '\xa7', - 'sect;': '\xa7', - 'semi;': ';', - 'seswar;': '\u2929', - 'setminus;': '\u2216', - 'setmn;': '\u2216', - 'sext;': '\u2736', - 'Sfr;': '\U0001d516', - 'sfr;': '\U0001d530', - 'sfrown;': '\u2322', - 'sharp;': '\u266f', - 'SHCHcy;': '\u0429', - 'shchcy;': '\u0449', - 'SHcy;': '\u0428', - 'shcy;': '\u0448', - 'ShortDownArrow;': '\u2193', - 'ShortLeftArrow;': '\u2190', - 'shortmid;': '\u2223', - 'shortparallel;': '\u2225', - 'ShortRightArrow;': '\u2192', - 'ShortUpArrow;': '\u2191', - 'shy': '\xad', - 'shy;': '\xad', - 'Sigma;': '\u03a3', - 'sigma;': '\u03c3', - 'sigmaf;': '\u03c2', - 'sigmav;': '\u03c2', - 'sim;': '\u223c', - 'simdot;': '\u2a6a', - 'sime;': '\u2243', - 'simeq;': '\u2243', - 'simg;': '\u2a9e', - 'simgE;': '\u2aa0', - 'siml;': '\u2a9d', - 'simlE;': '\u2a9f', - 'simne;': '\u2246', - 'simplus;': '\u2a24', - 'simrarr;': '\u2972', - 'slarr;': '\u2190', - 'SmallCircle;': '\u2218', - 'smallsetminus;': '\u2216', - 'smashp;': '\u2a33', - 'smeparsl;': '\u29e4', - 'smid;': '\u2223', - 'smile;': '\u2323', - 'smt;': '\u2aaa', - 'smte;': '\u2aac', - 'smtes;': '\u2aac\ufe00', - 'SOFTcy;': '\u042c', - 'softcy;': '\u044c', - 'sol;': '/', - 'solb;': '\u29c4', - 'solbar;': '\u233f', - 'Sopf;': '\U0001d54a', - 'sopf;': '\U0001d564', - 'spades;': '\u2660', - 'spadesuit;': '\u2660', - 'spar;': '\u2225', - 'sqcap;': '\u2293', - 'sqcaps;': '\u2293\ufe00', - 'sqcup;': '\u2294', - 'sqcups;': '\u2294\ufe00', - 'Sqrt;': '\u221a', - 'sqsub;': '\u228f', - 'sqsube;': '\u2291', - 'sqsubset;': '\u228f', - 'sqsubseteq;': '\u2291', - 'sqsup;': '\u2290', - 'sqsupe;': '\u2292', - 'sqsupset;': '\u2290', - 'sqsupseteq;': '\u2292', - 'squ;': '\u25a1', - 'Square;': '\u25a1', - 'square;': '\u25a1', - 'SquareIntersection;': '\u2293', - 'SquareSubset;': '\u228f', - 'SquareSubsetEqual;': '\u2291', - 'SquareSuperset;': '\u2290', - 'SquareSupersetEqual;': '\u2292', - 'SquareUnion;': '\u2294', - 'squarf;': '\u25aa', - 'squf;': '\u25aa', - 'srarr;': '\u2192', - 'Sscr;': '\U0001d4ae', - 'sscr;': '\U0001d4c8', - 'ssetmn;': '\u2216', - 'ssmile;': '\u2323', - 'sstarf;': '\u22c6', - 'Star;': '\u22c6', - 'star;': '\u2606', - 'starf;': '\u2605', - 'straightepsilon;': '\u03f5', - 'straightphi;': '\u03d5', - 'strns;': '\xaf', - 'Sub;': '\u22d0', - 'sub;': '\u2282', - 'subdot;': '\u2abd', - 'subE;': '\u2ac5', - 'sube;': '\u2286', - 'subedot;': '\u2ac3', - 'submult;': '\u2ac1', - 'subnE;': '\u2acb', - 'subne;': '\u228a', - 'subplus;': '\u2abf', - 'subrarr;': '\u2979', - 'Subset;': '\u22d0', - 'subset;': '\u2282', - 'subseteq;': '\u2286', - 'subseteqq;': '\u2ac5', - 'SubsetEqual;': '\u2286', - 'subsetneq;': '\u228a', - 'subsetneqq;': '\u2acb', - 'subsim;': '\u2ac7', - 'subsub;': '\u2ad5', - 'subsup;': '\u2ad3', - 'succ;': '\u227b', - 'succapprox;': '\u2ab8', - 'succcurlyeq;': '\u227d', - 'Succeeds;': '\u227b', - 'SucceedsEqual;': '\u2ab0', - 'SucceedsSlantEqual;': '\u227d', - 'SucceedsTilde;': '\u227f', - 'succeq;': '\u2ab0', - 'succnapprox;': '\u2aba', - 'succneqq;': '\u2ab6', - 'succnsim;': '\u22e9', - 'succsim;': '\u227f', - 'SuchThat;': '\u220b', - 'Sum;': '\u2211', - 'sum;': '\u2211', - 'sung;': '\u266a', - 'sup1': '\xb9', - 'sup1;': '\xb9', - 'sup2': '\xb2', - 'sup2;': '\xb2', - 'sup3': '\xb3', - 'sup3;': '\xb3', - 'Sup;': '\u22d1', - 'sup;': '\u2283', - 'supdot;': '\u2abe', - 'supdsub;': '\u2ad8', - 'supE;': '\u2ac6', - 'supe;': '\u2287', - 'supedot;': '\u2ac4', - 'Superset;': '\u2283', - 'SupersetEqual;': '\u2287', - 'suphsol;': '\u27c9', - 'suphsub;': '\u2ad7', - 'suplarr;': '\u297b', - 'supmult;': '\u2ac2', - 'supnE;': '\u2acc', - 'supne;': '\u228b', - 'supplus;': '\u2ac0', - 'Supset;': '\u22d1', - 'supset;': '\u2283', - 'supseteq;': '\u2287', - 'supseteqq;': '\u2ac6', - 'supsetneq;': '\u228b', - 'supsetneqq;': '\u2acc', - 'supsim;': '\u2ac8', - 'supsub;': '\u2ad4', - 'supsup;': '\u2ad6', - 'swarhk;': '\u2926', - 'swArr;': '\u21d9', - 'swarr;': '\u2199', - 'swarrow;': '\u2199', - 'swnwar;': '\u292a', - 'szlig': '\xdf', - 'szlig;': '\xdf', - 'Tab;': '\t', - 'target;': '\u2316', - 'Tau;': '\u03a4', - 'tau;': '\u03c4', - 'tbrk;': '\u23b4', - 'Tcaron;': '\u0164', - 'tcaron;': '\u0165', - 'Tcedil;': '\u0162', - 'tcedil;': '\u0163', - 'Tcy;': '\u0422', - 'tcy;': '\u0442', - 'tdot;': '\u20db', - 'telrec;': '\u2315', - 'Tfr;': '\U0001d517', - 'tfr;': '\U0001d531', - 'there4;': '\u2234', - 'Therefore;': '\u2234', - 'therefore;': '\u2234', - 'Theta;': '\u0398', - 'theta;': '\u03b8', - 'thetasym;': '\u03d1', - 'thetav;': '\u03d1', - 'thickapprox;': '\u2248', - 'thicksim;': '\u223c', - 'ThickSpace;': '\u205f\u200a', - 'thinsp;': '\u2009', - 'ThinSpace;': '\u2009', - 'thkap;': '\u2248', - 'thksim;': '\u223c', - 'THORN': '\xde', - 'thorn': '\xfe', - 'THORN;': '\xde', - 'thorn;': '\xfe', - 'Tilde;': '\u223c', - 'tilde;': '\u02dc', - 'TildeEqual;': '\u2243', - 'TildeFullEqual;': '\u2245', - 'TildeTilde;': '\u2248', - 'times': '\xd7', - 'times;': '\xd7', - 'timesb;': '\u22a0', - 'timesbar;': '\u2a31', - 'timesd;': '\u2a30', - 'tint;': '\u222d', - 'toea;': '\u2928', - 'top;': '\u22a4', - 'topbot;': '\u2336', - 'topcir;': '\u2af1', - 'Topf;': '\U0001d54b', - 'topf;': '\U0001d565', - 'topfork;': '\u2ada', - 'tosa;': '\u2929', - 'tprime;': '\u2034', - 'TRADE;': '\u2122', - 'trade;': '\u2122', - 'triangle;': '\u25b5', - 'triangledown;': '\u25bf', - 'triangleleft;': '\u25c3', - 'trianglelefteq;': '\u22b4', - 'triangleq;': '\u225c', - 'triangleright;': '\u25b9', - 'trianglerighteq;': '\u22b5', - 'tridot;': '\u25ec', - 'trie;': '\u225c', - 'triminus;': '\u2a3a', - 'TripleDot;': '\u20db', - 'triplus;': '\u2a39', - 'trisb;': '\u29cd', - 'tritime;': '\u2a3b', - 'trpezium;': '\u23e2', - 'Tscr;': '\U0001d4af', - 'tscr;': '\U0001d4c9', - 'TScy;': '\u0426', - 'tscy;': '\u0446', - 'TSHcy;': '\u040b', - 'tshcy;': '\u045b', - 'Tstrok;': '\u0166', - 'tstrok;': '\u0167', - 'twixt;': '\u226c', - 'twoheadleftarrow;': '\u219e', - 'twoheadrightarrow;': '\u21a0', - 'Uacute': '\xda', - 'uacute': '\xfa', - 'Uacute;': '\xda', - 'uacute;': '\xfa', - 'Uarr;': '\u219f', - 'uArr;': '\u21d1', - 'uarr;': '\u2191', - 'Uarrocir;': '\u2949', - 'Ubrcy;': '\u040e', - 'ubrcy;': '\u045e', - 'Ubreve;': '\u016c', - 'ubreve;': '\u016d', - 'Ucirc': '\xdb', - 'ucirc': '\xfb', - 'Ucirc;': '\xdb', - 'ucirc;': '\xfb', - 'Ucy;': '\u0423', - 'ucy;': '\u0443', - 'udarr;': '\u21c5', - 'Udblac;': '\u0170', - 'udblac;': '\u0171', - 'udhar;': '\u296e', - 'ufisht;': '\u297e', - 'Ufr;': '\U0001d518', - 'ufr;': '\U0001d532', - 'Ugrave': '\xd9', - 'ugrave': '\xf9', - 'Ugrave;': '\xd9', - 'ugrave;': '\xf9', - 'uHar;': '\u2963', - 'uharl;': '\u21bf', - 'uharr;': '\u21be', - 'uhblk;': '\u2580', - 'ulcorn;': '\u231c', - 'ulcorner;': '\u231c', - 'ulcrop;': '\u230f', - 'ultri;': '\u25f8', - 'Umacr;': '\u016a', - 'umacr;': '\u016b', - 'uml': '\xa8', - 'uml;': '\xa8', - 'UnderBar;': '_', - 'UnderBrace;': '\u23df', - 'UnderBracket;': '\u23b5', - 'UnderParenthesis;': '\u23dd', - 'Union;': '\u22c3', - 'UnionPlus;': '\u228e', - 'Uogon;': '\u0172', - 'uogon;': '\u0173', - 'Uopf;': '\U0001d54c', - 'uopf;': '\U0001d566', - 'UpArrow;': '\u2191', - 'Uparrow;': '\u21d1', - 'uparrow;': '\u2191', - 'UpArrowBar;': '\u2912', - 'UpArrowDownArrow;': '\u21c5', - 'UpDownArrow;': '\u2195', - 'Updownarrow;': '\u21d5', - 'updownarrow;': '\u2195', - 'UpEquilibrium;': '\u296e', - 'upharpoonleft;': '\u21bf', - 'upharpoonright;': '\u21be', - 'uplus;': '\u228e', - 'UpperLeftArrow;': '\u2196', - 'UpperRightArrow;': '\u2197', - 'Upsi;': '\u03d2', - 'upsi;': '\u03c5', - 'upsih;': '\u03d2', - 'Upsilon;': '\u03a5', - 'upsilon;': '\u03c5', - 'UpTee;': '\u22a5', - 'UpTeeArrow;': '\u21a5', - 'upuparrows;': '\u21c8', - 'urcorn;': '\u231d', - 'urcorner;': '\u231d', - 'urcrop;': '\u230e', - 'Uring;': '\u016e', - 'uring;': '\u016f', - 'urtri;': '\u25f9', - 'Uscr;': '\U0001d4b0', - 'uscr;': '\U0001d4ca', - 'utdot;': '\u22f0', - 'Utilde;': '\u0168', - 'utilde;': '\u0169', - 'utri;': '\u25b5', - 'utrif;': '\u25b4', - 'uuarr;': '\u21c8', - 'Uuml': '\xdc', - 'uuml': '\xfc', - 'Uuml;': '\xdc', - 'uuml;': '\xfc', - 'uwangle;': '\u29a7', - 'vangrt;': '\u299c', - 'varepsilon;': '\u03f5', - 'varkappa;': '\u03f0', - 'varnothing;': '\u2205', - 'varphi;': '\u03d5', - 'varpi;': '\u03d6', - 'varpropto;': '\u221d', - 'vArr;': '\u21d5', - 'varr;': '\u2195', - 'varrho;': '\u03f1', - 'varsigma;': '\u03c2', - 'varsubsetneq;': '\u228a\ufe00', - 'varsubsetneqq;': '\u2acb\ufe00', - 'varsupsetneq;': '\u228b\ufe00', - 'varsupsetneqq;': '\u2acc\ufe00', - 'vartheta;': '\u03d1', - 'vartriangleleft;': '\u22b2', - 'vartriangleright;': '\u22b3', - 'Vbar;': '\u2aeb', - 'vBar;': '\u2ae8', - 'vBarv;': '\u2ae9', - 'Vcy;': '\u0412', - 'vcy;': '\u0432', - 'VDash;': '\u22ab', - 'Vdash;': '\u22a9', - 'vDash;': '\u22a8', - 'vdash;': '\u22a2', - 'Vdashl;': '\u2ae6', - 'Vee;': '\u22c1', - 'vee;': '\u2228', - 'veebar;': '\u22bb', - 'veeeq;': '\u225a', - 'vellip;': '\u22ee', - 'Verbar;': '\u2016', - 'verbar;': '|', - 'Vert;': '\u2016', - 'vert;': '|', - 'VerticalBar;': '\u2223', - 'VerticalLine;': '|', - 'VerticalSeparator;': '\u2758', - 'VerticalTilde;': '\u2240', - 'VeryThinSpace;': '\u200a', - 'Vfr;': '\U0001d519', - 'vfr;': '\U0001d533', - 'vltri;': '\u22b2', - 'vnsub;': '\u2282\u20d2', - 'vnsup;': '\u2283\u20d2', - 'Vopf;': '\U0001d54d', - 'vopf;': '\U0001d567', - 'vprop;': '\u221d', - 'vrtri;': '\u22b3', - 'Vscr;': '\U0001d4b1', - 'vscr;': '\U0001d4cb', - 'vsubnE;': '\u2acb\ufe00', - 'vsubne;': '\u228a\ufe00', - 'vsupnE;': '\u2acc\ufe00', - 'vsupne;': '\u228b\ufe00', - 'Vvdash;': '\u22aa', - 'vzigzag;': '\u299a', - 'Wcirc;': '\u0174', - 'wcirc;': '\u0175', - 'wedbar;': '\u2a5f', - 'Wedge;': '\u22c0', - 'wedge;': '\u2227', - 'wedgeq;': '\u2259', - 'weierp;': '\u2118', - 'Wfr;': '\U0001d51a', - 'wfr;': '\U0001d534', - 'Wopf;': '\U0001d54e', - 'wopf;': '\U0001d568', - 'wp;': '\u2118', - 'wr;': '\u2240', - 'wreath;': '\u2240', - 'Wscr;': '\U0001d4b2', - 'wscr;': '\U0001d4cc', - 'xcap;': '\u22c2', - 'xcirc;': '\u25ef', - 'xcup;': '\u22c3', - 'xdtri;': '\u25bd', - 'Xfr;': '\U0001d51b', - 'xfr;': '\U0001d535', - 'xhArr;': '\u27fa', - 'xharr;': '\u27f7', - 'Xi;': '\u039e', - 'xi;': '\u03be', - 'xlArr;': '\u27f8', - 'xlarr;': '\u27f5', - 'xmap;': '\u27fc', - 'xnis;': '\u22fb', - 'xodot;': '\u2a00', - 'Xopf;': '\U0001d54f', - 'xopf;': '\U0001d569', - 'xoplus;': '\u2a01', - 'xotime;': '\u2a02', - 'xrArr;': '\u27f9', - 'xrarr;': '\u27f6', - 'Xscr;': '\U0001d4b3', - 'xscr;': '\U0001d4cd', - 'xsqcup;': '\u2a06', - 'xuplus;': '\u2a04', - 'xutri;': '\u25b3', - 'xvee;': '\u22c1', - 'xwedge;': '\u22c0', - 'Yacute': '\xdd', - 'yacute': '\xfd', - 'Yacute;': '\xdd', - 'yacute;': '\xfd', - 'YAcy;': '\u042f', - 'yacy;': '\u044f', - 'Ycirc;': '\u0176', - 'ycirc;': '\u0177', - 'Ycy;': '\u042b', - 'ycy;': '\u044b', - 'yen': '\xa5', - 'yen;': '\xa5', - 'Yfr;': '\U0001d51c', - 'yfr;': '\U0001d536', - 'YIcy;': '\u0407', - 'yicy;': '\u0457', - 'Yopf;': '\U0001d550', - 'yopf;': '\U0001d56a', - 'Yscr;': '\U0001d4b4', - 'yscr;': '\U0001d4ce', - 'YUcy;': '\u042e', - 'yucy;': '\u044e', - 'yuml': '\xff', - 'Yuml;': '\u0178', - 'yuml;': '\xff', - 'Zacute;': '\u0179', - 'zacute;': '\u017a', - 'Zcaron;': '\u017d', - 'zcaron;': '\u017e', - 'Zcy;': '\u0417', - 'zcy;': '\u0437', - 'Zdot;': '\u017b', - 'zdot;': '\u017c', - 'zeetrf;': '\u2128', - 'ZeroWidthSpace;': '\u200b', - 'Zeta;': '\u0396', - 'zeta;': '\u03b6', - 'Zfr;': '\u2128', - 'zfr;': '\U0001d537', - 'ZHcy;': '\u0416', - 'zhcy;': '\u0436', - 'zigrarr;': '\u21dd', - 'Zopf;': '\u2124', - 'zopf;': '\U0001d56b', - 'Zscr;': '\U0001d4b5', - 'zscr;': '\U0001d4cf', - 'zwj;': '\u200d', - 'zwnj;': '\u200c', -} - -# maps the Unicode codepoint to the HTML entity name -codepoint2name = {} - -# maps the HTML entity name to the character -# (or a character reference if the character is outside the Latin-1 range) -entitydefs = {} - -for (name, codepoint) in name2codepoint.items(): - codepoint2name[codepoint] = name - entitydefs[name] = chr(codepoint) - -del name, codepoint - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/html/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/html/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/html/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/html/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -""" -General functions for HTML manipulation, backported from Py3. - -Note that this uses Python 2.7 code with the corresponding Python 3 -module names and locations. -""" - -from __future__ import unicode_literals - - -_escape_map = {ord('&'): '&', ord('<'): '<', ord('>'): '>'} -_escape_map_full = {ord('&'): '&', ord('<'): '<', ord('>'): '>', - ord('"'): '"', ord('\''): '''} - -# NB: this is a candidate for a bytes/string polymorphic interface - -def escape(s, quote=True): - """ - Replace special characters "&", "<" and ">" to HTML-safe sequences. - If the optional flag quote is true (the default), the quotation mark - characters, both double quote (") and single quote (') characters are also - translated. - """ - assert not isinstance(s, bytes), 'Pass a unicode string' - if quote: - return s.translate(_escape_map_full) - return s.translate(_escape_map) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/html/parser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/html/parser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/html/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/html/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,537 +0,0 @@ -"""A parser for HTML and XHTML. - -Backported for python-future from Python 3.3. -""" - -# This file is based on sgmllib.py, but the API is slightly different. - -# XXX There should be a way to distinguish between PCDATA (parsed -# character data -- the normal case), RCDATA (replaceable character -# data -- only char and entity references and end tags are special) -# and CDATA (character data -- only end tags are special). - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import * -from future.backports import _markupbase -import re -import warnings - -# Regular expressions used for parsing - -interesting_normal = re.compile('[&<]') -incomplete = re.compile('&[a-zA-Z#]') - -entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]') -charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]') - -starttagopen = re.compile('<[a-zA-Z]') -piclose = re.compile('>') -commentclose = re.compile(r'--\s*>') -tagfind = re.compile('([a-zA-Z][-.a-zA-Z0-9:_]*)(?:\s|/(?!>))*') -# see http://www.w3.org/TR/html5/tokenization.html#tag-open-state -# and http://www.w3.org/TR/html5/tokenization.html#tag-name-state -tagfind_tolerant = re.compile('[a-zA-Z][^\t\n\r\f />\x00]*') -# Note: -# 1) the strict attrfind isn't really strict, but we can't make it -# correctly strict without breaking backward compatibility; -# 2) if you change attrfind remember to update locatestarttagend too; -# 3) if you change attrfind and/or locatestarttagend the parser will -# explode, so don't do it. -attrfind = re.compile( - r'\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*' - r'(\'[^\']*\'|"[^"]*"|[^\s"\'=<>`]*))?') -attrfind_tolerant = re.compile( - r'((?<=[\'"\s/])[^\s/>][^\s/=>]*)(\s*=+\s*' - r'(\'[^\']*\'|"[^"]*"|(?![\'"])[^>\s]*))?(?:\s|/(?!>))*') -locatestarttagend = re.compile(r""" - <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name - (?:\s+ # whitespace before attribute name - (?:[a-zA-Z_][-.:a-zA-Z0-9_]* # attribute name - (?:\s*=\s* # value indicator - (?:'[^']*' # LITA-enclosed value - |\"[^\"]*\" # LIT-enclosed value - |[^'\">\s]+ # bare value - ) - )? - ) - )* - \s* # trailing whitespace -""", re.VERBOSE) -locatestarttagend_tolerant = re.compile(r""" - <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name - (?:[\s/]* # optional whitespace before attribute name - (?:(?<=['"\s/])[^\s/>][^\s/=>]* # attribute name - (?:\s*=+\s* # value indicator - (?:'[^']*' # LITA-enclosed value - |"[^"]*" # LIT-enclosed value - |(?!['"])[^>\s]* # bare value - ) - (?:\s*,)* # possibly followed by a comma - )?(?:\s|/(?!>))* - )* - )? - \s* # trailing whitespace -""", re.VERBOSE) -endendtag = re.compile('>') -# the HTML 5 spec, section 8.1.2.2, doesn't allow spaces between -# ') - - -class HTMLParseError(Exception): - """Exception raised for all parse errors.""" - - def __init__(self, msg, position=(None, None)): - assert msg - self.msg = msg - self.lineno = position[0] - self.offset = position[1] - - def __str__(self): - result = self.msg - if self.lineno is not None: - result = result + ", at line %d" % self.lineno - if self.offset is not None: - result = result + ", column %d" % (self.offset + 1) - return result - - -class HTMLParser(_markupbase.ParserBase): - """Find tags and other markup and call handler functions. - - Usage: - p = HTMLParser() - p.feed(data) - ... - p.close() - - Start tags are handled by calling self.handle_starttag() or - self.handle_startendtag(); end tags by self.handle_endtag(). The - data between tags is passed from the parser to the derived class - by calling self.handle_data() with the data as argument (the data - may be split up in arbitrary chunks). Entity references are - passed by calling self.handle_entityref() with the entity - reference as the argument. Numeric character references are - passed to self.handle_charref() with the string containing the - reference as the argument. - """ - - CDATA_CONTENT_ELEMENTS = ("script", "style") - - def __init__(self, strict=False): - """Initialize and reset this instance. - - If strict is set to False (the default) the parser will parse invalid - markup, otherwise it will raise an error. Note that the strict mode - is deprecated. - """ - if strict: - warnings.warn("The strict mode is deprecated.", - DeprecationWarning, stacklevel=2) - self.strict = strict - self.reset() - - def reset(self): - """Reset this instance. Loses all unprocessed data.""" - self.rawdata = '' - self.lasttag = '???' - self.interesting = interesting_normal - self.cdata_elem = None - _markupbase.ParserBase.reset(self) - - def feed(self, data): - r"""Feed data to the parser. - - Call this as often as you want, with as little or as much text - as you want (may include '\n'). - """ - self.rawdata = self.rawdata + data - self.goahead(0) - - def close(self): - """Handle any buffered data.""" - self.goahead(1) - - def error(self, message): - raise HTMLParseError(message, self.getpos()) - - __starttag_text = None - - def get_starttag_text(self): - """Return full source of start tag: '<...>'.""" - return self.__starttag_text - - def set_cdata_mode(self, elem): - self.cdata_elem = elem.lower() - self.interesting = re.compile(r'' % self.cdata_elem, re.I) - - def clear_cdata_mode(self): - self.interesting = interesting_normal - self.cdata_elem = None - - # Internal -- handle data as far as reasonable. May leave state - # and data to be processed by a subsequent call. If 'end' is - # true, force handling all data as if followed by EOF marker. - def goahead(self, end): - rawdata = self.rawdata - i = 0 - n = len(rawdata) - while i < n: - match = self.interesting.search(rawdata, i) # < or & - if match: - j = match.start() - else: - if self.cdata_elem: - break - j = n - if i < j: self.handle_data(rawdata[i:j]) - i = self.updatepos(i, j) - if i == n: break - startswith = rawdata.startswith - if startswith('<', i): - if starttagopen.match(rawdata, i): # < + letter - k = self.parse_starttag(i) - elif startswith("', i + 1) - if k < 0: - k = rawdata.find('<', i + 1) - if k < 0: - k = i + 1 - else: - k += 1 - self.handle_data(rawdata[i:k]) - i = self.updatepos(i, k) - elif startswith("&#", i): - match = charref.match(rawdata, i) - if match: - name = match.group()[2:-1] - self.handle_charref(name) - k = match.end() - if not startswith(';', k-1): - k = k - 1 - i = self.updatepos(i, k) - continue - else: - if ";" in rawdata[i:]: #bail by consuming &# - self.handle_data(rawdata[0:2]) - i = self.updatepos(i, 2) - break - elif startswith('&', i): - match = entityref.match(rawdata, i) - if match: - name = match.group(1) - self.handle_entityref(name) - k = match.end() - if not startswith(';', k-1): - k = k - 1 - i = self.updatepos(i, k) - continue - match = incomplete.match(rawdata, i) - if match: - # match.group() will contain at least 2 chars - if end and match.group() == rawdata[i:]: - if self.strict: - self.error("EOF in middle of entity or char ref") - else: - if k <= i: - k = n - i = self.updatepos(i, i + 1) - # incomplete - break - elif (i + 1) < n: - # not the end of the buffer, and can't be confused - # with some other construct - self.handle_data("&") - i = self.updatepos(i, i + 1) - else: - break - else: - assert 0, "interesting.search() lied" - # end while - if end and i < n and not self.cdata_elem: - self.handle_data(rawdata[i:n]) - i = self.updatepos(i, n) - self.rawdata = rawdata[i:] - - # Internal -- parse html declarations, return length or -1 if not terminated - # See w3.org/TR/html5/tokenization.html#markup-declaration-open-state - # See also parse_declaration in _markupbase - def parse_html_declaration(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == ' - gtpos = rawdata.find('>', i+9) - if gtpos == -1: - return -1 - self.handle_decl(rawdata[i+2:gtpos]) - return gtpos+1 - else: - return self.parse_bogus_comment(i) - - # Internal -- parse bogus comment, return length or -1 if not terminated - # see http://www.w3.org/TR/html5/tokenization.html#bogus-comment-state - def parse_bogus_comment(self, i, report=1): - rawdata = self.rawdata - assert rawdata[i:i+2] in ('', i+2) - if pos == -1: - return -1 - if report: - self.handle_comment(rawdata[i+2:pos]) - return pos + 1 - - # Internal -- parse processing instr, return end or -1 if not terminated - def parse_pi(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == ' - if not match: - return -1 - j = match.start() - self.handle_pi(rawdata[i+2: j]) - j = match.end() - return j - - # Internal -- handle starttag, return end or -1 if not terminated - def parse_starttag(self, i): - self.__starttag_text = None - endpos = self.check_for_whole_start_tag(i) - if endpos < 0: - return endpos - rawdata = self.rawdata - self.__starttag_text = rawdata[i:endpos] - - # Now parse the data between i+1 and j into a tag and attrs - attrs = [] - match = tagfind.match(rawdata, i+1) - assert match, 'unexpected call to parse_starttag()' - k = match.end() - self.lasttag = tag = match.group(1).lower() - while k < endpos: - if self.strict: - m = attrfind.match(rawdata, k) - else: - m = attrfind_tolerant.match(rawdata, k) - if not m: - break - attrname, rest, attrvalue = m.group(1, 2, 3) - if not rest: - attrvalue = None - elif attrvalue[:1] == '\'' == attrvalue[-1:] or \ - attrvalue[:1] == '"' == attrvalue[-1:]: - attrvalue = attrvalue[1:-1] - if attrvalue: - attrvalue = self.unescape(attrvalue) - attrs.append((attrname.lower(), attrvalue)) - k = m.end() - - end = rawdata[k:endpos].strip() - if end not in (">", "/>"): - lineno, offset = self.getpos() - if "\n" in self.__starttag_text: - lineno = lineno + self.__starttag_text.count("\n") - offset = len(self.__starttag_text) \ - - self.__starttag_text.rfind("\n") - else: - offset = offset + len(self.__starttag_text) - if self.strict: - self.error("junk characters in start tag: %r" - % (rawdata[k:endpos][:20],)) - self.handle_data(rawdata[i:endpos]) - return endpos - if end.endswith('/>'): - # XHTML-style empty tag: - self.handle_startendtag(tag, attrs) - else: - self.handle_starttag(tag, attrs) - if tag in self.CDATA_CONTENT_ELEMENTS: - self.set_cdata_mode(tag) - return endpos - - # Internal -- check to see if we have a complete starttag; return end - # or -1 if incomplete. - def check_for_whole_start_tag(self, i): - rawdata = self.rawdata - if self.strict: - m = locatestarttagend.match(rawdata, i) - else: - m = locatestarttagend_tolerant.match(rawdata, i) - if m: - j = m.end() - next = rawdata[j:j+1] - if next == ">": - return j + 1 - if next == "/": - if rawdata.startswith("/>", j): - return j + 2 - if rawdata.startswith("/", j): - # buffer boundary - return -1 - # else bogus input - if self.strict: - self.updatepos(i, j + 1) - self.error("malformed empty start tag") - if j > i: - return j - else: - return i + 1 - if next == "": - # end of input - return -1 - if next in ("abcdefghijklmnopqrstuvwxyz=/" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"): - # end of input in or before attribute value, or we have the - # '/' from a '/>' ending - return -1 - if self.strict: - self.updatepos(i, j) - self.error("malformed start tag") - if j > i: - return j - else: - return i + 1 - raise AssertionError("we should not get here!") - - # Internal -- parse endtag, return end or -1 if incomplete - def parse_endtag(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == " - if not match: - return -1 - gtpos = match.end() - match = endtagfind.match(rawdata, i) # - if not match: - if self.cdata_elem is not None: - self.handle_data(rawdata[i:gtpos]) - return gtpos - if self.strict: - self.error("bad end tag: %r" % (rawdata[i:gtpos],)) - # find the name: w3.org/TR/html5/tokenization.html#tag-name-state - namematch = tagfind_tolerant.match(rawdata, i+2) - if not namematch: - # w3.org/TR/html5/tokenization.html#end-tag-open-state - if rawdata[i:i+3] == '': - return i+3 - else: - return self.parse_bogus_comment(i) - tagname = namematch.group().lower() - # consume and ignore other stuff between the name and the > - # Note: this is not 100% correct, since we might have things like - # , but looking for > after tha name should cover - # most of the cases and is much simpler - gtpos = rawdata.find('>', namematch.end()) - self.handle_endtag(tagname) - return gtpos+1 - - elem = match.group(1).lower() # script or style - if self.cdata_elem is not None: - if elem != self.cdata_elem: - self.handle_data(rawdata[i:gtpos]) - return gtpos - - self.handle_endtag(elem.lower()) - self.clear_cdata_mode() - return gtpos - - # Overridable -- finish processing of start+end tag: - def handle_startendtag(self, tag, attrs): - self.handle_starttag(tag, attrs) - self.handle_endtag(tag) - - # Overridable -- handle start tag - def handle_starttag(self, tag, attrs): - pass - - # Overridable -- handle end tag - def handle_endtag(self, tag): - pass - - # Overridable -- handle character reference - def handle_charref(self, name): - pass - - # Overridable -- handle entity reference - def handle_entityref(self, name): - pass - - # Overridable -- handle data - def handle_data(self, data): - pass - - # Overridable -- handle comment - def handle_comment(self, data): - pass - - # Overridable -- handle declaration - def handle_decl(self, decl): - pass - - # Overridable -- handle processing instruction - def handle_pi(self, data): - pass - - def unknown_decl(self, data): - if self.strict: - self.error("unknown declaration: %r" % (data,)) - - # Internal -- helper to remove special character quoting - def unescape(self, s): - if '&' not in s: - return s - def replaceEntities(s): - s = s.groups()[0] - try: - if s[0] == "#": - s = s[1:] - if s[0] in ['x','X']: - c = int(s[1:].rstrip(';'), 16) - else: - c = int(s.rstrip(';')) - return chr(c) - except ValueError: - return '&#' + s - else: - from future.backports.html.entities import html5 - if s in html5: - return html5[s] - elif s.endswith(';'): - return '&' + s - for x in range(2, len(s)): - if s[:x] in html5: - return html5[s[:x]] + s[x:] - else: - return '&' + s - - return re.sub(r"&(#?[xX]?(?:[0-9a-fA-F]+;|\w{1,32};?))", - replaceEntities, s) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/client.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/client.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1332 +0,0 @@ -"""HTTP/1.1 client library - -A backport of the Python 3.3 http/client.py module for python-future. - - - - -HTTPConnection goes through a number of "states", which define when a client -may legally make another request or fetch the response for a particular -request. This diagram details these state transitions: - - (null) - | - | HTTPConnection() - v - Idle - | - | putrequest() - v - Request-started - | - | ( putheader() )* endheaders() - v - Request-sent - | - | response = getresponse() - v - Unread-response [Response-headers-read] - |\____________________ - | | - | response.read() | putrequest() - v v - Idle Req-started-unread-response - ______/| - / | - response.read() | | ( putheader() )* endheaders() - v v - Request-started Req-sent-unread-response - | - | response.read() - v - Request-sent - -This diagram presents the following rules: - -- a second request may not be started until {response-headers-read} - -- a response [object] cannot be retrieved until {request-sent} - -- there is no differentiation between an unread response body and a - partially read response body - -Note: this enforcement is applied by the HTTPConnection class. The - HTTPResponse class does not enforce this state machine, which - implies sophisticated clients may accelerate the request/response - pipeline. Caution should be taken, though: accelerating the states - beyond the above pattern may imply knowledge of the server's - connection-close behavior for certain requests. For example, it - is impossible to tell whether the server will close the connection - UNTIL the response headers have been read; this means that further - requests cannot be placed into the pipeline until it is known that - the server will NOT be closing the connection. - -Logical State __state __response -------------- ------- ---------- -Idle _CS_IDLE None -Request-started _CS_REQ_STARTED None -Request-sent _CS_REQ_SENT None -Unread-response _CS_IDLE -Req-started-unread-response _CS_REQ_STARTED -Req-sent-unread-response _CS_REQ_SENT -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import bytes, int, str, super -from future.utils import PY2 - -from future.backports.email import parser as email_parser -from future.backports.email import message as email_message -from future.backports.misc import create_connection as socket_create_connection -import io -import os -import socket -import collections -from future.backports.urllib.parse import urlsplit -import warnings -from array import array - -__all__ = ["HTTPResponse", "HTTPConnection", - "HTTPException", "NotConnected", "UnknownProtocol", - "UnknownTransferEncoding", "UnimplementedFileMode", - "IncompleteRead", "InvalidURL", "ImproperConnectionState", - "CannotSendRequest", "CannotSendHeader", "ResponseNotReady", - "BadStatusLine", "error", "responses"] - -HTTP_PORT = 80 -HTTPS_PORT = 443 - -_UNKNOWN = 'UNKNOWN' - -# connection states -_CS_IDLE = 'Idle' -_CS_REQ_STARTED = 'Request-started' -_CS_REQ_SENT = 'Request-sent' - -# status codes -# informational -CONTINUE = 100 -SWITCHING_PROTOCOLS = 101 -PROCESSING = 102 - -# successful -OK = 200 -CREATED = 201 -ACCEPTED = 202 -NON_AUTHORITATIVE_INFORMATION = 203 -NO_CONTENT = 204 -RESET_CONTENT = 205 -PARTIAL_CONTENT = 206 -MULTI_STATUS = 207 -IM_USED = 226 - -# redirection -MULTIPLE_CHOICES = 300 -MOVED_PERMANENTLY = 301 -FOUND = 302 -SEE_OTHER = 303 -NOT_MODIFIED = 304 -USE_PROXY = 305 -TEMPORARY_REDIRECT = 307 - -# client error -BAD_REQUEST = 400 -UNAUTHORIZED = 401 -PAYMENT_REQUIRED = 402 -FORBIDDEN = 403 -NOT_FOUND = 404 -METHOD_NOT_ALLOWED = 405 -NOT_ACCEPTABLE = 406 -PROXY_AUTHENTICATION_REQUIRED = 407 -REQUEST_TIMEOUT = 408 -CONFLICT = 409 -GONE = 410 -LENGTH_REQUIRED = 411 -PRECONDITION_FAILED = 412 -REQUEST_ENTITY_TOO_LARGE = 413 -REQUEST_URI_TOO_LONG = 414 -UNSUPPORTED_MEDIA_TYPE = 415 -REQUESTED_RANGE_NOT_SATISFIABLE = 416 -EXPECTATION_FAILED = 417 -UNPROCESSABLE_ENTITY = 422 -LOCKED = 423 -FAILED_DEPENDENCY = 424 -UPGRADE_REQUIRED = 426 -PRECONDITION_REQUIRED = 428 -TOO_MANY_REQUESTS = 429 -REQUEST_HEADER_FIELDS_TOO_LARGE = 431 - -# server error -INTERNAL_SERVER_ERROR = 500 -NOT_IMPLEMENTED = 501 -BAD_GATEWAY = 502 -SERVICE_UNAVAILABLE = 503 -GATEWAY_TIMEOUT = 504 -HTTP_VERSION_NOT_SUPPORTED = 505 -INSUFFICIENT_STORAGE = 507 -NOT_EXTENDED = 510 -NETWORK_AUTHENTICATION_REQUIRED = 511 - -# Mapping status codes to official W3C names -responses = { - 100: 'Continue', - 101: 'Switching Protocols', - - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non-Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - - 300: 'Multiple Choices', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 306: '(Unused)', - 307: 'Temporary Redirect', - - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Request Entity Too Large', - 414: 'Request-URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Requested Range Not Satisfiable', - 417: 'Expectation Failed', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 511: 'Network Authentication Required', -} - -# maximal amount of data to read at one time in _safe_read -MAXAMOUNT = 1048576 - -# maximal line length when calling readline(). -_MAXLINE = 65536 -_MAXHEADERS = 100 - - -class HTTPMessage(email_message.Message): - # XXX The only usage of this method is in - # http.server.CGIHTTPRequestHandler. Maybe move the code there so - # that it doesn't need to be part of the public API. The API has - # never been defined so this could cause backwards compatibility - # issues. - - def getallmatchingheaders(self, name): - """Find all header lines matching a given header name. - - Look through the list of headers and find all lines matching a given - header name (and their continuation lines). A list of the lines is - returned, without interpretation. If the header does not occur, an - empty list is returned. If the header occurs multiple times, all - occurrences are returned. Case is not important in the header name. - - """ - name = name.lower() + ':' - n = len(name) - lst = [] - hit = 0 - for line in self.keys(): - if line[:n].lower() == name: - hit = 1 - elif not line[:1].isspace(): - hit = 0 - if hit: - lst.append(line) - return lst - -def parse_headers(fp, _class=HTTPMessage): - """Parses only RFC2822 headers from a file pointer. - - email Parser wants to see strings rather than bytes. - But a TextIOWrapper around self.rfile would buffer too many bytes - from the stream, bytes which we later need to read as bytes. - So we read the correct bytes here, as bytes, for email Parser - to parse. - - """ - headers = [] - while True: - line = fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("header line") - headers.append(line) - if len(headers) > _MAXHEADERS: - raise HTTPException("got more than %d headers" % _MAXHEADERS) - if line in (b'\r\n', b'\n', b''): - break - hstring = bytes(b'').join(headers).decode('iso-8859-1') - return email_parser.Parser(_class=_class).parsestr(hstring) - - -_strict_sentinel = object() - -class HTTPResponse(io.RawIOBase): - - # See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details. - - # The bytes from the socket object are iso-8859-1 strings. - # See RFC 2616 sec 2.2 which notes an exception for MIME-encoded - # text following RFC 2047. The basic status line parsing only - # accepts iso-8859-1. - - def __init__(self, sock, debuglevel=0, strict=_strict_sentinel, method=None, url=None): - # If the response includes a content-length header, we need to - # make sure that the client doesn't read more than the - # specified number of bytes. If it does, it will block until - # the server times out and closes the connection. This will - # happen if a self.fp.read() is done (without a size) whether - # self.fp is buffered or not. So, no self.fp.read() by - # clients unless they know what they are doing. - self.fp = sock.makefile("rb") - self.debuglevel = debuglevel - if strict is not _strict_sentinel: - warnings.warn("the 'strict' argument isn't supported anymore; " - "http.client now always assumes HTTP/1.x compliant servers.", - DeprecationWarning, 2) - self._method = method - - # The HTTPResponse object is returned via urllib. The clients - # of http and urllib expect different attributes for the - # headers. headers is used here and supports urllib. msg is - # provided as a backwards compatibility layer for http - # clients. - - self.headers = self.msg = None - - # from the Status-Line of the response - self.version = _UNKNOWN # HTTP-Version - self.status = _UNKNOWN # Status-Code - self.reason = _UNKNOWN # Reason-Phrase - - self.chunked = _UNKNOWN # is "chunked" being used? - self.chunk_left = _UNKNOWN # bytes left to read in current chunk - self.length = _UNKNOWN # number of bytes left in response - self.will_close = _UNKNOWN # conn will close at end of response - - def _read_status(self): - line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") - if len(line) > _MAXLINE: - raise LineTooLong("status line") - if self.debuglevel > 0: - print("reply:", repr(line)) - if not line: - # Presumably, the server closed the connection before - # sending a valid response. - raise BadStatusLine(line) - try: - version, status, reason = line.split(None, 2) - except ValueError: - try: - version, status = line.split(None, 1) - reason = "" - except ValueError: - # empty version will cause next test to fail. - version = "" - if not version.startswith("HTTP/"): - self._close_conn() - raise BadStatusLine(line) - - # The status code is a three-digit number - try: - status = int(status) - if status < 100 or status > 999: - raise BadStatusLine(line) - except ValueError: - raise BadStatusLine(line) - return version, status, reason - - def begin(self): - if self.headers is not None: - # we've already started reading the response - return - - # read until we get a non-100 response - while True: - version, status, reason = self._read_status() - if status != CONTINUE: - break - # skip the header from the 100 response - while True: - skip = self.fp.readline(_MAXLINE + 1) - if len(skip) > _MAXLINE: - raise LineTooLong("header line") - skip = skip.strip() - if not skip: - break - if self.debuglevel > 0: - print("header:", skip) - - self.code = self.status = status - self.reason = reason.strip() - if version in ("HTTP/1.0", "HTTP/0.9"): - # Some servers might still return "0.9", treat it as 1.0 anyway - self.version = 10 - elif version.startswith("HTTP/1."): - self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1 - else: - raise UnknownProtocol(version) - - self.headers = self.msg = parse_headers(self.fp) - - if self.debuglevel > 0: - for hdr in self.headers: - print("header:", hdr, end=" ") - - # are we using the chunked-style of transfer encoding? - tr_enc = self.headers.get("transfer-encoding") - if tr_enc and tr_enc.lower() == "chunked": - self.chunked = True - self.chunk_left = None - else: - self.chunked = False - - # will the connection close at the end of the response? - self.will_close = self._check_close() - - # do we have a Content-Length? - # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" - self.length = None - length = self.headers.get("content-length") - - # are we using the chunked-style of transfer encoding? - tr_enc = self.headers.get("transfer-encoding") - if length and not self.chunked: - try: - self.length = int(length) - except ValueError: - self.length = None - else: - if self.length < 0: # ignore nonsensical negative lengths - self.length = None - else: - self.length = None - - # does the body have a fixed length? (of zero) - if (status == NO_CONTENT or status == NOT_MODIFIED or - 100 <= status < 200 or # 1xx codes - self._method == "HEAD"): - self.length = 0 - - # if the connection remains open, and we aren't using chunked, and - # a content-length was not provided, then assume that the connection - # WILL close. - if (not self.will_close and - not self.chunked and - self.length is None): - self.will_close = True - - def _check_close(self): - conn = self.headers.get("connection") - if self.version == 11: - # An HTTP/1.1 proxy is assumed to stay open unless - # explicitly closed. - conn = self.headers.get("connection") - if conn and "close" in conn.lower(): - return True - return False - - # Some HTTP/1.0 implementations have support for persistent - # connections, using rules different than HTTP/1.1. - - # For older HTTP, Keep-Alive indicates persistent connection. - if self.headers.get("keep-alive"): - return False - - # At least Akamai returns a "Connection: Keep-Alive" header, - # which was supposed to be sent by the client. - if conn and "keep-alive" in conn.lower(): - return False - - # Proxy-Connection is a netscape hack. - pconn = self.headers.get("proxy-connection") - if pconn and "keep-alive" in pconn.lower(): - return False - - # otherwise, assume it will close - return True - - def _close_conn(self): - fp = self.fp - self.fp = None - fp.close() - - def close(self): - super().close() # set "closed" flag - if self.fp: - self._close_conn() - - # These implementations are for the benefit of io.BufferedReader. - - # XXX This class should probably be revised to act more like - # the "raw stream" that BufferedReader expects. - - def flush(self): - super().flush() - if self.fp: - self.fp.flush() - - def readable(self): - return True - - # End of "raw stream" methods - - def isclosed(self): - """True if the connection is closed.""" - # NOTE: it is possible that we will not ever call self.close(). This - # case occurs when will_close is TRUE, length is None, and we - # read up to the last byte, but NOT past it. - # - # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be - # called, meaning self.isclosed() is meaningful. - return self.fp is None - - def read(self, amt=None): - if self.fp is None: - return bytes(b"") - - if self._method == "HEAD": - self._close_conn() - return bytes(b"") - - if amt is not None: - # Amount is given, so call base class version - # (which is implemented in terms of self.readinto) - return bytes(super(HTTPResponse, self).read(amt)) - else: - # Amount is not given (unbounded read) so we must check self.length - # and self.chunked - - if self.chunked: - return self._readall_chunked() - - if self.length is None: - s = self.fp.read() - else: - try: - s = self._safe_read(self.length) - except IncompleteRead: - self._close_conn() - raise - self.length = 0 - self._close_conn() # we read everything - return bytes(s) - - def readinto(self, b): - if self.fp is None: - return 0 - - if self._method == "HEAD": - self._close_conn() - return 0 - - if self.chunked: - return self._readinto_chunked(b) - - if self.length is not None: - if len(b) > self.length: - # clip the read to the "end of response" - b = memoryview(b)[0:self.length] - - # we do not use _safe_read() here because this may be a .will_close - # connection, and the user is reading more bytes than will be provided - # (for example, reading in 1k chunks) - - if PY2: - data = self.fp.read(len(b)) - n = len(data) - b[:n] = data - else: - n = self.fp.readinto(b) - - if not n and b: - # Ideally, we would raise IncompleteRead if the content-length - # wasn't satisfied, but it might break compatibility. - self._close_conn() - elif self.length is not None: - self.length -= n - if not self.length: - self._close_conn() - return n - - def _read_next_chunk_size(self): - # Read the next chunk size from the file - line = self.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("chunk size") - i = line.find(b";") - if i >= 0: - line = line[:i] # strip chunk-extensions - try: - return int(line, 16) - except ValueError: - # close the connection as protocol synchronisation is - # probably lost - self._close_conn() - raise - - def _read_and_discard_trailer(self): - # read and discard trailer up to the CRLF terminator - ### note: we shouldn't have any trailers! - while True: - line = self.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("trailer line") - if not line: - # a vanishingly small number of sites EOF without - # sending the trailer - break - if line in (b'\r\n', b'\n', b''): - break - - def _readall_chunked(self): - assert self.chunked != _UNKNOWN - chunk_left = self.chunk_left - value = [] - while True: - if chunk_left is None: - try: - chunk_left = self._read_next_chunk_size() - if chunk_left == 0: - break - except ValueError: - raise IncompleteRead(bytes(b'').join(value)) - value.append(self._safe_read(chunk_left)) - - # we read the whole chunk, get another - self._safe_read(2) # toss the CRLF at the end of the chunk - chunk_left = None - - self._read_and_discard_trailer() - - # we read everything; close the "file" - self._close_conn() - - return bytes(b'').join(value) - - def _readinto_chunked(self, b): - assert self.chunked != _UNKNOWN - chunk_left = self.chunk_left - - total_bytes = 0 - mvb = memoryview(b) - while True: - if chunk_left is None: - try: - chunk_left = self._read_next_chunk_size() - if chunk_left == 0: - break - except ValueError: - raise IncompleteRead(bytes(b[0:total_bytes])) - - if len(mvb) < chunk_left: - n = self._safe_readinto(mvb) - self.chunk_left = chunk_left - n - return total_bytes + n - elif len(mvb) == chunk_left: - n = self._safe_readinto(mvb) - self._safe_read(2) # toss the CRLF at the end of the chunk - self.chunk_left = None - return total_bytes + n - else: - temp_mvb = mvb[0:chunk_left] - n = self._safe_readinto(temp_mvb) - mvb = mvb[n:] - total_bytes += n - - # we read the whole chunk, get another - self._safe_read(2) # toss the CRLF at the end of the chunk - chunk_left = None - - self._read_and_discard_trailer() - - # we read everything; close the "file" - self._close_conn() - - return total_bytes - - def _safe_read(self, amt): - """Read the number of bytes requested, compensating for partial reads. - - Normally, we have a blocking socket, but a read() can be interrupted - by a signal (resulting in a partial read). - - Note that we cannot distinguish between EOF and an interrupt when zero - bytes have been read. IncompleteRead() will be raised in this - situation. - - This function should be used when bytes "should" be present for - reading. If the bytes are truly not available (due to EOF), then the - IncompleteRead exception can be used to detect the problem. - """ - s = [] - while amt > 0: - chunk = self.fp.read(min(amt, MAXAMOUNT)) - if not chunk: - raise IncompleteRead(bytes(b'').join(s), amt) - s.append(chunk) - amt -= len(chunk) - return bytes(b"").join(s) - - def _safe_readinto(self, b): - """Same as _safe_read, but for reading into a buffer.""" - total_bytes = 0 - mvb = memoryview(b) - while total_bytes < len(b): - if MAXAMOUNT < len(mvb): - temp_mvb = mvb[0:MAXAMOUNT] - n = self.fp.readinto(temp_mvb) - else: - n = self.fp.readinto(mvb) - if not n: - raise IncompleteRead(bytes(mvb[0:total_bytes]), len(b)) - mvb = mvb[n:] - total_bytes += n - return total_bytes - - def fileno(self): - return self.fp.fileno() - - def getheader(self, name, default=None): - if self.headers is None: - raise ResponseNotReady() - headers = self.headers.get_all(name) or default - if isinstance(headers, str) or not hasattr(headers, '__iter__'): - return headers - else: - return ', '.join(headers) - - def getheaders(self): - """Return list of (header, value) tuples.""" - if self.headers is None: - raise ResponseNotReady() - return list(self.headers.items()) - - # We override IOBase.__iter__ so that it doesn't check for closed-ness - - def __iter__(self): - return self - - # For compatibility with old-style urllib responses. - - def info(self): - return self.headers - - def geturl(self): - return self.url - - def getcode(self): - return self.status - -class HTTPConnection(object): - - _http_vsn = 11 - _http_vsn_str = 'HTTP/1.1' - - response_class = HTTPResponse - default_port = HTTP_PORT - auto_open = 1 - debuglevel = 0 - - def __init__(self, host, port=None, strict=_strict_sentinel, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None): - if strict is not _strict_sentinel: - warnings.warn("the 'strict' argument isn't supported anymore; " - "http.client now always assumes HTTP/1.x compliant servers.", - DeprecationWarning, 2) - self.timeout = timeout - self.source_address = source_address - self.sock = None - self._buffer = [] - self.__response = None - self.__state = _CS_IDLE - self._method = None - self._tunnel_host = None - self._tunnel_port = None - self._tunnel_headers = {} - - self._set_hostport(host, port) - - def set_tunnel(self, host, port=None, headers=None): - """ Sets up the host and the port for the HTTP CONNECT Tunnelling. - - The headers argument should be a mapping of extra HTTP headers - to send with the CONNECT request. - """ - self._tunnel_host = host - self._tunnel_port = port - if headers: - self._tunnel_headers = headers - else: - self._tunnel_headers.clear() - - def _set_hostport(self, host, port): - if port is None: - i = host.rfind(':') - j = host.rfind(']') # ipv6 addresses have [...] - if i > j: - try: - port = int(host[i+1:]) - except ValueError: - if host[i+1:] == "": # http://foo.com:/ == http://foo.com/ - port = self.default_port - else: - raise InvalidURL("nonnumeric port: '%s'" % host[i+1:]) - host = host[:i] - else: - port = self.default_port - if host and host[0] == '[' and host[-1] == ']': - host = host[1:-1] - self.host = host - self.port = port - - def set_debuglevel(self, level): - self.debuglevel = level - - def _tunnel(self): - self._set_hostport(self._tunnel_host, self._tunnel_port) - connect_str = "CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port) - connect_bytes = connect_str.encode("ascii") - self.send(connect_bytes) - for header, value in self._tunnel_headers.items(): - header_str = "%s: %s\r\n" % (header, value) - header_bytes = header_str.encode("latin-1") - self.send(header_bytes) - self.send(bytes(b'\r\n')) - - response = self.response_class(self.sock, method=self._method) - (version, code, message) = response._read_status() - - if code != 200: - self.close() - raise socket.error("Tunnel connection failed: %d %s" % (code, - message.strip())) - while True: - line = response.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("header line") - if not line: - # for sites which EOF without sending a trailer - break - if line in (b'\r\n', b'\n', b''): - break - - def connect(self): - """Connect to the host and port specified in __init__.""" - self.sock = socket_create_connection((self.host,self.port), - self.timeout, self.source_address) - if self._tunnel_host: - self._tunnel() - - def close(self): - """Close the connection to the HTTP server.""" - if self.sock: - self.sock.close() # close it manually... there may be other refs - self.sock = None - if self.__response: - self.__response.close() - self.__response = None - self.__state = _CS_IDLE - - def send(self, data): - """Send `data' to the server. - ``data`` can be a string object, a bytes object, an array object, a - file-like object that supports a .read() method, or an iterable object. - """ - - if self.sock is None: - if self.auto_open: - self.connect() - else: - raise NotConnected() - - if self.debuglevel > 0: - print("send:", repr(data)) - blocksize = 8192 - # Python 2.7 array objects have a read method which is incompatible - # with the 2-arg calling syntax below. - if hasattr(data, "read") and not isinstance(data, array): - if self.debuglevel > 0: - print("sendIng a read()able") - encode = False - try: - mode = data.mode - except AttributeError: - # io.BytesIO and other file-like objects don't have a `mode` - # attribute. - pass - else: - if "b" not in mode: - encode = True - if self.debuglevel > 0: - print("encoding file using iso-8859-1") - while 1: - datablock = data.read(blocksize) - if not datablock: - break - if encode: - datablock = datablock.encode("iso-8859-1") - self.sock.sendall(datablock) - return - try: - self.sock.sendall(data) - except TypeError: - if isinstance(data, collections.Iterable): - for d in data: - self.sock.sendall(d) - else: - raise TypeError("data should be a bytes-like object " - "or an iterable, got %r" % type(data)) - - def _output(self, s): - """Add a line of output to the current request buffer. - - Assumes that the line does *not* end with \\r\\n. - """ - self._buffer.append(s) - - def _send_output(self, message_body=None): - """Send the currently buffered request and clear the buffer. - - Appends an extra \\r\\n to the buffer. - A message_body may be specified, to be appended to the request. - """ - self._buffer.extend((bytes(b""), bytes(b""))) - msg = bytes(b"\r\n").join(self._buffer) - del self._buffer[:] - # If msg and message_body are sent in a single send() call, - # it will avoid performance problems caused by the interaction - # between delayed ack and the Nagle algorithm. - if isinstance(message_body, bytes): - msg += message_body - message_body = None - self.send(msg) - if message_body is not None: - # message_body was not a string (i.e. it is a file), and - # we must run the risk of Nagle. - self.send(message_body) - - def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): - """Send a request to the server. - - `method' specifies an HTTP request method, e.g. 'GET'. - `url' specifies the object being requested, e.g. '/index.html'. - `skip_host' if True does not add automatically a 'Host:' header - `skip_accept_encoding' if True does not add automatically an - 'Accept-Encoding:' header - """ - - # if a prior response has been completed, then forget about it. - if self.__response and self.__response.isclosed(): - self.__response = None - - - # in certain cases, we cannot issue another request on this connection. - # this occurs when: - # 1) we are in the process of sending a request. (_CS_REQ_STARTED) - # 2) a response to a previous request has signalled that it is going - # to close the connection upon completion. - # 3) the headers for the previous response have not been read, thus - # we cannot determine whether point (2) is true. (_CS_REQ_SENT) - # - # if there is no prior response, then we can request at will. - # - # if point (2) is true, then we will have passed the socket to the - # response (effectively meaning, "there is no prior response"), and - # will open a new one when a new request is made. - # - # Note: if a prior response exists, then we *can* start a new request. - # We are not allowed to begin fetching the response to this new - # request, however, until that prior response is complete. - # - if self.__state == _CS_IDLE: - self.__state = _CS_REQ_STARTED - else: - raise CannotSendRequest(self.__state) - - # Save the method we use, we need it later in the response phase - self._method = method - if not url: - url = '/' - request = '%s %s %s' % (method, url, self._http_vsn_str) - - # Non-ASCII characters should have been eliminated earlier - self._output(request.encode('ascii')) - - if self._http_vsn == 11: - # Issue some standard headers for better HTTP/1.1 compliance - - if not skip_host: - # this header is issued *only* for HTTP/1.1 - # connections. more specifically, this means it is - # only issued when the client uses the new - # HTTPConnection() class. backwards-compat clients - # will be using HTTP/1.0 and those clients may be - # issuing this header themselves. we should NOT issue - # it twice; some web servers (such as Apache) barf - # when they see two Host: headers - - # If we need a non-standard port,include it in the - # header. If the request is going through a proxy, - # but the host of the actual URL, not the host of the - # proxy. - - netloc = '' - if url.startswith('http'): - nil, netloc, nil, nil, nil = urlsplit(url) - - if netloc: - try: - netloc_enc = netloc.encode("ascii") - except UnicodeEncodeError: - netloc_enc = netloc.encode("idna") - self.putheader('Host', netloc_enc) - else: - try: - host_enc = self.host.encode("ascii") - except UnicodeEncodeError: - host_enc = self.host.encode("idna") - - # As per RFC 273, IPv6 address should be wrapped with [] - # when used as Host header - - if self.host.find(':') >= 0: - host_enc = bytes(b'[' + host_enc + b']') - - if self.port == self.default_port: - self.putheader('Host', host_enc) - else: - host_enc = host_enc.decode("ascii") - self.putheader('Host', "%s:%s" % (host_enc, self.port)) - - # note: we are assuming that clients will not attempt to set these - # headers since *this* library must deal with the - # consequences. this also means that when the supporting - # libraries are updated to recognize other forms, then this - # code should be changed (removed or updated). - - # we only want a Content-Encoding of "identity" since we don't - # support encodings such as x-gzip or x-deflate. - if not skip_accept_encoding: - self.putheader('Accept-Encoding', 'identity') - - # we can accept "chunked" Transfer-Encodings, but no others - # NOTE: no TE header implies *only* "chunked" - #self.putheader('TE', 'chunked') - - # if TE is supplied in the header, then it must appear in a - # Connection header. - #self.putheader('Connection', 'TE') - - else: - # For HTTP/1.0, the server will assume "not chunked" - pass - - def putheader(self, header, *values): - """Send a request header line to the server. - - For example: h.putheader('Accept', 'text/html') - """ - if self.__state != _CS_REQ_STARTED: - raise CannotSendHeader() - - if hasattr(header, 'encode'): - header = header.encode('ascii') - values = list(values) - for i, one_value in enumerate(values): - if hasattr(one_value, 'encode'): - values[i] = one_value.encode('latin-1') - elif isinstance(one_value, int): - values[i] = str(one_value).encode('ascii') - value = bytes(b'\r\n\t').join(values) - header = header + bytes(b': ') + value - self._output(header) - - def endheaders(self, message_body=None): - """Indicate that the last header line has been sent to the server. - - This method sends the request to the server. The optional message_body - argument can be used to pass a message body associated with the - request. The message body will be sent in the same packet as the - message headers if it is a string, otherwise it is sent as a separate - packet. - """ - if self.__state == _CS_REQ_STARTED: - self.__state = _CS_REQ_SENT - else: - raise CannotSendHeader() - self._send_output(message_body) - - def request(self, method, url, body=None, headers={}): - """Send a complete request to the server.""" - self._send_request(method, url, body, headers) - - def _set_content_length(self, body): - # Set the content-length based on the body. - thelen = None - try: - thelen = str(len(body)) - except TypeError as te: - # If this is a file-like object, try to - # fstat its file descriptor - try: - thelen = str(os.fstat(body.fileno()).st_size) - except (AttributeError, OSError): - # Don't send a length if this failed - if self.debuglevel > 0: print("Cannot stat!!") - - if thelen is not None: - self.putheader('Content-Length', thelen) - - def _send_request(self, method, url, body, headers): - # Honor explicitly requested Host: and Accept-Encoding: headers. - header_names = dict.fromkeys([k.lower() for k in headers]) - skips = {} - if 'host' in header_names: - skips['skip_host'] = 1 - if 'accept-encoding' in header_names: - skips['skip_accept_encoding'] = 1 - - self.putrequest(method, url, **skips) - - if body is not None and ('content-length' not in header_names): - self._set_content_length(body) - for hdr, value in headers.items(): - self.putheader(hdr, value) - if isinstance(body, str): - # RFC 2616 Section 3.7.1 says that text default has a - # default charset of iso-8859-1. - body = body.encode('iso-8859-1') - self.endheaders(body) - - def getresponse(self): - """Get the response from the server. - - If the HTTPConnection is in the correct state, returns an - instance of HTTPResponse or of whatever object is returned by - class the response_class variable. - - If a request has not been sent or if a previous response has - not be handled, ResponseNotReady is raised. If the HTTP - response indicates that the connection should be closed, then - it will be closed before the response is returned. When the - connection is closed, the underlying socket is closed. - """ - - # if a prior response has been completed, then forget about it. - if self.__response and self.__response.isclosed(): - self.__response = None - - # if a prior response exists, then it must be completed (otherwise, we - # cannot read this response's header to determine the connection-close - # behavior) - # - # note: if a prior response existed, but was connection-close, then the - # socket and response were made independent of this HTTPConnection - # object since a new request requires that we open a whole new - # connection - # - # this means the prior response had one of two states: - # 1) will_close: this connection was reset and the prior socket and - # response operate independently - # 2) persistent: the response was retained and we await its - # isclosed() status to become true. - # - if self.__state != _CS_REQ_SENT or self.__response: - raise ResponseNotReady(self.__state) - - if self.debuglevel > 0: - response = self.response_class(self.sock, self.debuglevel, - method=self._method) - else: - response = self.response_class(self.sock, method=self._method) - - response.begin() - assert response.will_close != _UNKNOWN - self.__state = _CS_IDLE - - if response.will_close: - # this effectively passes the connection to the response - self.close() - else: - # remember this, so we can tell when it is complete - self.__response = response - - return response - -try: - import ssl - from ssl import SSLContext -except ImportError: - pass -else: - class HTTPSConnection(HTTPConnection): - "This class allows communication via SSL." - - default_port = HTTPS_PORT - - # XXX Should key_file and cert_file be deprecated in favour of context? - - def __init__(self, host, port=None, key_file=None, cert_file=None, - strict=_strict_sentinel, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - source_address=None, **_3to2kwargs): - if 'check_hostname' in _3to2kwargs: check_hostname = _3to2kwargs['check_hostname']; del _3to2kwargs['check_hostname'] - else: check_hostname = None - if 'context' in _3to2kwargs: context = _3to2kwargs['context']; del _3to2kwargs['context'] - else: context = None - super(HTTPSConnection, self).__init__(host, port, strict, timeout, - source_address) - self.key_file = key_file - self.cert_file = cert_file - if context is None: - # Some reasonable defaults - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - will_verify = context.verify_mode != ssl.CERT_NONE - if check_hostname is None: - check_hostname = will_verify - elif check_hostname and not will_verify: - raise ValueError("check_hostname needs a SSL context with " - "either CERT_OPTIONAL or CERT_REQUIRED") - if key_file or cert_file: - context.load_cert_chain(cert_file, key_file) - self._context = context - self._check_hostname = check_hostname - - def connect(self): - "Connect to a host on a given (SSL) port." - - sock = socket_create_connection((self.host, self.port), - self.timeout, self.source_address) - - if self._tunnel_host: - self.sock = sock - self._tunnel() - - server_hostname = self.host if ssl.HAS_SNI else None - self.sock = self._context.wrap_socket(sock, - server_hostname=server_hostname) - try: - if self._check_hostname: - ssl.match_hostname(self.sock.getpeercert(), self.host) - except Exception: - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - raise - - __all__.append("HTTPSConnection") - - - # ###################################### - # # We use the old HTTPSConnection class from Py2.7, because ssl.SSLContext - # # doesn't exist in the Py2.7 stdlib - # class HTTPSConnection(HTTPConnection): - # "This class allows communication via SSL." - - # default_port = HTTPS_PORT - - # def __init__(self, host, port=None, key_file=None, cert_file=None, - # strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - # source_address=None): - # HTTPConnection.__init__(self, host, port, strict, timeout, - # source_address) - # self.key_file = key_file - # self.cert_file = cert_file - - # def connect(self): - # "Connect to a host on a given (SSL) port." - - # sock = socket_create_connection((self.host, self.port), - # self.timeout, self.source_address) - # if self._tunnel_host: - # self.sock = sock - # self._tunnel() - # self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file) - - # __all__.append("HTTPSConnection") - # ###################################### - - -class HTTPException(Exception): - # Subclasses that define an __init__ must call Exception.__init__ - # or define self.args. Otherwise, str() will fail. - pass - -class NotConnected(HTTPException): - pass - -class InvalidURL(HTTPException): - pass - -class UnknownProtocol(HTTPException): - def __init__(self, version): - self.args = version, - self.version = version - -class UnknownTransferEncoding(HTTPException): - pass - -class UnimplementedFileMode(HTTPException): - pass - -class IncompleteRead(HTTPException): - def __init__(self, partial, expected=None): - self.args = partial, - self.partial = partial - self.expected = expected - def __repr__(self): - if self.expected is not None: - e = ', %i more expected' % self.expected - else: - e = '' - return 'IncompleteRead(%i bytes read%s)' % (len(self.partial), e) - def __str__(self): - return repr(self) - -class ImproperConnectionState(HTTPException): - pass - -class CannotSendRequest(ImproperConnectionState): - pass - -class CannotSendHeader(ImproperConnectionState): - pass - -class ResponseNotReady(ImproperConnectionState): - pass - -class BadStatusLine(HTTPException): - def __init__(self, line): - if not line: - line = repr(line) - self.args = line, - self.line = line - -class LineTooLong(HTTPException): - def __init__(self, line_type): - HTTPException.__init__(self, "got more than %d bytes when reading %s" - % (_MAXLINE, line_type)) - -# for backwards compatibility -error = HTTPException diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/cookiejar.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/cookiejar.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/cookiejar.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/cookiejar.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2109 +0,0 @@ -r"""HTTP cookie handling for web clients. - -This is a backport of the Py3.3 ``http.cookiejar`` module for -python-future. - -This module has (now fairly distant) origins in Gisle Aas' Perl module -HTTP::Cookies, from the libwww-perl library. - -Docstrings, comments and debug strings in this code refer to the -attributes of the HTTP cookie system as cookie-attributes, to distinguish -them clearly from Python attributes. - -Class diagram (note that BSDDBCookieJar and the MSIE* classes are not -distributed with the Python standard library, but are available from -http://wwwsearch.sf.net/): - - CookieJar____ - / \ \ - FileCookieJar \ \ - / | \ \ \ - MozillaCookieJar | LWPCookieJar \ \ - | | \ - | ---MSIEBase | \ - | / | | \ - | / MSIEDBCookieJar BSDDBCookieJar - |/ - MSIECookieJar - -""" - -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import filter, int, map, open, str -from future.utils import as_native_str - -__all__ = ['Cookie', 'CookieJar', 'CookiePolicy', 'DefaultCookiePolicy', - 'FileCookieJar', 'LWPCookieJar', 'LoadError', 'MozillaCookieJar'] - -import copy -import datetime -import re -re.ASCII = 0 -import time -from future.backports.urllib.parse import urlparse, urlsplit, quote -from future.backports.http.client import HTTP_PORT -try: - import threading as _threading -except ImportError: - import dummy_threading as _threading -from calendar import timegm - -debug = False # set to True to enable debugging via the logging module -logger = None - -def _debug(*args): - if not debug: - return - global logger - if not logger: - import logging - logger = logging.getLogger("http.cookiejar") - return logger.debug(*args) - - -DEFAULT_HTTP_PORT = str(HTTP_PORT) -MISSING_FILENAME_TEXT = ("a filename was not supplied (nor was the CookieJar " - "instance initialised with one)") - -def _warn_unhandled_exception(): - # There are a few catch-all except: statements in this module, for - # catching input that's bad in unexpected ways. Warn if any - # exceptions are caught there. - import io, warnings, traceback - f = io.StringIO() - traceback.print_exc(None, f) - msg = f.getvalue() - warnings.warn("http.cookiejar bug!\n%s" % msg, stacklevel=2) - - -# Date/time conversion -# ----------------------------------------------------------------------------- - -EPOCH_YEAR = 1970 -def _timegm(tt): - year, month, mday, hour, min, sec = tt[:6] - if ((year >= EPOCH_YEAR) and (1 <= month <= 12) and (1 <= mday <= 31) and - (0 <= hour <= 24) and (0 <= min <= 59) and (0 <= sec <= 61)): - return timegm(tt) - else: - return None - -DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] -MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -MONTHS_LOWER = [] -for month in MONTHS: MONTHS_LOWER.append(month.lower()) - -def time2isoz(t=None): - """Return a string representing time in seconds since epoch, t. - - If the function is called without an argument, it will use the current - time. - - The format of the returned string is like "YYYY-MM-DD hh:mm:ssZ", - representing Universal Time (UTC, aka GMT). An example of this format is: - - 1994-11-24 08:49:37Z - - """ - if t is None: - dt = datetime.datetime.utcnow() - else: - dt = datetime.datetime.utcfromtimestamp(t) - return "%04d-%02d-%02d %02d:%02d:%02dZ" % ( - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) - -def time2netscape(t=None): - """Return a string representing time in seconds since epoch, t. - - If the function is called without an argument, it will use the current - time. - - The format of the returned string is like this: - - Wed, DD-Mon-YYYY HH:MM:SS GMT - - """ - if t is None: - dt = datetime.datetime.utcnow() - else: - dt = datetime.datetime.utcfromtimestamp(t) - return "%s %02d-%s-%04d %02d:%02d:%02d GMT" % ( - DAYS[dt.weekday()], dt.day, MONTHS[dt.month-1], - dt.year, dt.hour, dt.minute, dt.second) - - -UTC_ZONES = {"GMT": None, "UTC": None, "UT": None, "Z": None} - -TIMEZONE_RE = re.compile(r"^([-+])?(\d\d?):?(\d\d)?$", re.ASCII) -def offset_from_tz_string(tz): - offset = None - if tz in UTC_ZONES: - offset = 0 - else: - m = TIMEZONE_RE.search(tz) - if m: - offset = 3600 * int(m.group(2)) - if m.group(3): - offset = offset + 60 * int(m.group(3)) - if m.group(1) == '-': - offset = -offset - return offset - -def _str2time(day, mon, yr, hr, min, sec, tz): - # translate month name to number - # month numbers start with 1 (January) - try: - mon = MONTHS_LOWER.index(mon.lower())+1 - except ValueError: - # maybe it's already a number - try: - imon = int(mon) - except ValueError: - return None - if 1 <= imon <= 12: - mon = imon - else: - return None - - # make sure clock elements are defined - if hr is None: hr = 0 - if min is None: min = 0 - if sec is None: sec = 0 - - yr = int(yr) - day = int(day) - hr = int(hr) - min = int(min) - sec = int(sec) - - if yr < 1000: - # find "obvious" year - cur_yr = time.localtime(time.time())[0] - m = cur_yr % 100 - tmp = yr - yr = yr + cur_yr - m - m = m - tmp - if abs(m) > 50: - if m > 0: yr = yr + 100 - else: yr = yr - 100 - - # convert UTC time tuple to seconds since epoch (not timezone-adjusted) - t = _timegm((yr, mon, day, hr, min, sec, tz)) - - if t is not None: - # adjust time using timezone string, to get absolute time since epoch - if tz is None: - tz = "UTC" - tz = tz.upper() - offset = offset_from_tz_string(tz) - if offset is None: - return None - t = t - offset - - return t - -STRICT_DATE_RE = re.compile( - r"^[SMTWF][a-z][a-z], (\d\d) ([JFMASOND][a-z][a-z]) " - "(\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT$", re.ASCII) -WEEKDAY_RE = re.compile( - r"^(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[a-z]*,?\s*", re.I | re.ASCII) -LOOSE_HTTP_DATE_RE = re.compile( - r"""^ - (\d\d?) # day - (?:\s+|[-\/]) - (\w+) # month - (?:\s+|[-\/]) - (\d+) # year - (?: - (?:\s+|:) # separator before clock - (\d\d?):(\d\d) # hour:min - (?::(\d\d))? # optional seconds - )? # optional clock - \s* - ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+)? # timezone - \s* - (?:\(\w+\))? # ASCII representation of timezone in parens. - \s*$""", re.X | re.ASCII) -def http2time(text): - """Returns time in seconds since epoch of time represented by a string. - - Return value is an integer. - - None is returned if the format of str is unrecognized, the time is outside - the representable range, or the timezone string is not recognized. If the - string contains no timezone, UTC is assumed. - - The timezone in the string may be numerical (like "-0800" or "+0100") or a - string timezone (like "UTC", "GMT", "BST" or "EST"). Currently, only the - timezone strings equivalent to UTC (zero offset) are known to the function. - - The function loosely parses the following formats: - - Wed, 09 Feb 1994 22:23:32 GMT -- HTTP format - Tuesday, 08-Feb-94 14:15:29 GMT -- old rfc850 HTTP format - Tuesday, 08-Feb-1994 14:15:29 GMT -- broken rfc850 HTTP format - 09 Feb 1994 22:23:32 GMT -- HTTP format (no weekday) - 08-Feb-94 14:15:29 GMT -- rfc850 format (no weekday) - 08-Feb-1994 14:15:29 GMT -- broken rfc850 format (no weekday) - - The parser ignores leading and trailing whitespace. The time may be - absent. - - If the year is given with only 2 digits, the function will select the - century that makes the year closest to the current date. - - """ - # fast exit for strictly conforming string - m = STRICT_DATE_RE.search(text) - if m: - g = m.groups() - mon = MONTHS_LOWER.index(g[1].lower()) + 1 - tt = (int(g[2]), mon, int(g[0]), - int(g[3]), int(g[4]), float(g[5])) - return _timegm(tt) - - # No, we need some messy parsing... - - # clean up - text = text.lstrip() - text = WEEKDAY_RE.sub("", text, 1) # Useless weekday - - # tz is time zone specifier string - day, mon, yr, hr, min, sec, tz = [None]*7 - - # loose regexp parse - m = LOOSE_HTTP_DATE_RE.search(text) - if m is not None: - day, mon, yr, hr, min, sec, tz = m.groups() - else: - return None # bad format - - return _str2time(day, mon, yr, hr, min, sec, tz) - -ISO_DATE_RE = re.compile( - """^ - (\d{4}) # year - [-\/]? - (\d\d?) # numerical month - [-\/]? - (\d\d?) # day - (?: - (?:\s+|[-:Tt]) # separator before clock - (\d\d?):?(\d\d) # hour:min - (?::?(\d\d(?:\.\d*)?))? # optional seconds (and fractional) - )? # optional clock - \s* - ([-+]?\d\d?:?(:?\d\d)? - |Z|z)? # timezone (Z is "zero meridian", i.e. GMT) - \s*$""", re.X | re. ASCII) -def iso2time(text): - """ - As for http2time, but parses the ISO 8601 formats: - - 1994-02-03 14:15:29 -0100 -- ISO 8601 format - 1994-02-03 14:15:29 -- zone is optional - 1994-02-03 -- only date - 1994-02-03T14:15:29 -- Use T as separator - 19940203T141529Z -- ISO 8601 compact format - 19940203 -- only date - - """ - # clean up - text = text.lstrip() - - # tz is time zone specifier string - day, mon, yr, hr, min, sec, tz = [None]*7 - - # loose regexp parse - m = ISO_DATE_RE.search(text) - if m is not None: - # XXX there's an extra bit of the timezone I'm ignoring here: is - # this the right thing to do? - yr, mon, day, hr, min, sec, tz, _ = m.groups() - else: - return None # bad format - - return _str2time(day, mon, yr, hr, min, sec, tz) - - -# Header parsing -# ----------------------------------------------------------------------------- - -def unmatched(match): - """Return unmatched part of re.Match object.""" - start, end = match.span(0) - return match.string[:start]+match.string[end:] - -HEADER_TOKEN_RE = re.compile(r"^\s*([^=\s;,]+)") -HEADER_QUOTED_VALUE_RE = re.compile(r"^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"") -HEADER_VALUE_RE = re.compile(r"^\s*=\s*([^\s;,]*)") -HEADER_ESCAPE_RE = re.compile(r"\\(.)") -def split_header_words(header_values): - r"""Parse header values into a list of lists containing key,value pairs. - - The function knows how to deal with ",", ";" and "=" as well as quoted - values after "=". A list of space separated tokens are parsed as if they - were separated by ";". - - If the header_values passed as argument contains multiple values, then they - are treated as if they were a single value separated by comma ",". - - This means that this function is useful for parsing header fields that - follow this syntax (BNF as from the HTTP/1.1 specification, but we relax - the requirement for tokens). - - headers = #header - header = (token | parameter) *( [";"] (token | parameter)) - - token = 1* - separators = "(" | ")" | "<" | ">" | "@" - | "," | ";" | ":" | "\" | <"> - | "/" | "[" | "]" | "?" | "=" - | "{" | "}" | SP | HT - - quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) - qdtext = > - quoted-pair = "\" CHAR - - parameter = attribute "=" value - attribute = token - value = token | quoted-string - - Each header is represented by a list of key/value pairs. The value for a - simple token (not part of a parameter) is None. Syntactically incorrect - headers will not necessarily be parsed as you would want. - - This is easier to describe with some examples: - - >>> split_header_words(['foo="bar"; port="80,81"; discard, bar=baz']) - [[('foo', 'bar'), ('port', '80,81'), ('discard', None)], [('bar', 'baz')]] - >>> split_header_words(['text/html; charset="iso-8859-1"']) - [[('text/html', None), ('charset', 'iso-8859-1')]] - >>> split_header_words([r'Basic realm="\"foo\bar\""']) - [[('Basic', None), ('realm', '"foobar"')]] - - """ - assert not isinstance(header_values, str) - result = [] - for text in header_values: - orig_text = text - pairs = [] - while text: - m = HEADER_TOKEN_RE.search(text) - if m: - text = unmatched(m) - name = m.group(1) - m = HEADER_QUOTED_VALUE_RE.search(text) - if m: # quoted value - text = unmatched(m) - value = m.group(1) - value = HEADER_ESCAPE_RE.sub(r"\1", value) - else: - m = HEADER_VALUE_RE.search(text) - if m: # unquoted value - text = unmatched(m) - value = m.group(1) - value = value.rstrip() - else: - # no value, a lone token - value = None - pairs.append((name, value)) - elif text.lstrip().startswith(","): - # concatenated headers, as per RFC 2616 section 4.2 - text = text.lstrip()[1:] - if pairs: result.append(pairs) - pairs = [] - else: - # skip junk - non_junk, nr_junk_chars = re.subn("^[=\s;]*", "", text) - assert nr_junk_chars > 0, ( - "split_header_words bug: '%s', '%s', %s" % - (orig_text, text, pairs)) - text = non_junk - if pairs: result.append(pairs) - return result - -HEADER_JOIN_ESCAPE_RE = re.compile(r"([\"\\])") -def join_header_words(lists): - """Do the inverse (almost) of the conversion done by split_header_words. - - Takes a list of lists of (key, value) pairs and produces a single header - value. Attribute values are quoted if needed. - - >>> join_header_words([[("text/plain", None), ("charset", "iso-8859/1")]]) - 'text/plain; charset="iso-8859/1"' - >>> join_header_words([[("text/plain", None)], [("charset", "iso-8859/1")]]) - 'text/plain, charset="iso-8859/1"' - - """ - headers = [] - for pairs in lists: - attr = [] - for k, v in pairs: - if v is not None: - if not re.search(r"^\w+$", v): - v = HEADER_JOIN_ESCAPE_RE.sub(r"\\\1", v) # escape " and \ - v = '"%s"' % v - k = "%s=%s" % (k, v) - attr.append(k) - if attr: headers.append("; ".join(attr)) - return ", ".join(headers) - -def strip_quotes(text): - if text.startswith('"'): - text = text[1:] - if text.endswith('"'): - text = text[:-1] - return text - -def parse_ns_headers(ns_headers): - """Ad-hoc parser for Netscape protocol cookie-attributes. - - The old Netscape cookie format for Set-Cookie can for instance contain - an unquoted "," in the expires field, so we have to use this ad-hoc - parser instead of split_header_words. - - XXX This may not make the best possible effort to parse all the crap - that Netscape Cookie headers contain. Ronald Tschalar's HTTPClient - parser is probably better, so could do worse than following that if - this ever gives any trouble. - - Currently, this is also used for parsing RFC 2109 cookies. - - """ - known_attrs = ("expires", "domain", "path", "secure", - # RFC 2109 attrs (may turn up in Netscape cookies, too) - "version", "port", "max-age") - - result = [] - for ns_header in ns_headers: - pairs = [] - version_set = False - for ii, param in enumerate(re.split(r";\s*", ns_header)): - param = param.rstrip() - if param == "": continue - if "=" not in param: - k, v = param, None - else: - k, v = re.split(r"\s*=\s*", param, 1) - k = k.lstrip() - if ii != 0: - lc = k.lower() - if lc in known_attrs: - k = lc - if k == "version": - # This is an RFC 2109 cookie. - v = strip_quotes(v) - version_set = True - if k == "expires": - # convert expires date to seconds since epoch - v = http2time(strip_quotes(v)) # None if invalid - pairs.append((k, v)) - - if pairs: - if not version_set: - pairs.append(("version", "0")) - result.append(pairs) - - return result - - -IPV4_RE = re.compile(r"\.\d+$", re.ASCII) -def is_HDN(text): - """Return True if text is a host domain name.""" - # XXX - # This may well be wrong. Which RFC is HDN defined in, if any (for - # the purposes of RFC 2965)? - # For the current implementation, what about IPv6? Remember to look - # at other uses of IPV4_RE also, if change this. - if IPV4_RE.search(text): - return False - if text == "": - return False - if text[0] == "." or text[-1] == ".": - return False - return True - -def domain_match(A, B): - """Return True if domain A domain-matches domain B, according to RFC 2965. - - A and B may be host domain names or IP addresses. - - RFC 2965, section 1: - - Host names can be specified either as an IP address or a HDN string. - Sometimes we compare one host name with another. (Such comparisons SHALL - be case-insensitive.) Host A's name domain-matches host B's if - - * their host name strings string-compare equal; or - - * A is a HDN string and has the form NB, where N is a non-empty - name string, B has the form .B', and B' is a HDN string. (So, - x.y.com domain-matches .Y.com but not Y.com.) - - Note that domain-match is not a commutative operation: a.b.c.com - domain-matches .c.com, but not the reverse. - - """ - # Note that, if A or B are IP addresses, the only relevant part of the - # definition of the domain-match algorithm is the direct string-compare. - A = A.lower() - B = B.lower() - if A == B: - return True - if not is_HDN(A): - return False - i = A.rfind(B) - if i == -1 or i == 0: - # A does not have form NB, or N is the empty string - return False - if not B.startswith("."): - return False - if not is_HDN(B[1:]): - return False - return True - -def liberal_is_HDN(text): - """Return True if text is a sort-of-like a host domain name. - - For accepting/blocking domains. - - """ - if IPV4_RE.search(text): - return False - return True - -def user_domain_match(A, B): - """For blocking/accepting domains. - - A and B may be host domain names or IP addresses. - - """ - A = A.lower() - B = B.lower() - if not (liberal_is_HDN(A) and liberal_is_HDN(B)): - if A == B: - # equal IP addresses - return True - return False - initial_dot = B.startswith(".") - if initial_dot and A.endswith(B): - return True - if not initial_dot and A == B: - return True - return False - -cut_port_re = re.compile(r":\d+$", re.ASCII) -def request_host(request): - """Return request-host, as defined by RFC 2965. - - Variation from RFC: returned value is lowercased, for convenient - comparison. - - """ - url = request.get_full_url() - host = urlparse(url)[1] - if host == "": - host = request.get_header("Host", "") - - # remove port, if present - host = cut_port_re.sub("", host, 1) - return host.lower() - -def eff_request_host(request): - """Return a tuple (request-host, effective request-host name). - - As defined by RFC 2965, except both are lowercased. - - """ - erhn = req_host = request_host(request) - if req_host.find(".") == -1 and not IPV4_RE.search(req_host): - erhn = req_host + ".local" - return req_host, erhn - -def request_path(request): - """Path component of request-URI, as defined by RFC 2965.""" - url = request.get_full_url() - parts = urlsplit(url) - path = escape_path(parts.path) - if not path.startswith("/"): - # fix bad RFC 2396 absoluteURI - path = "/" + path - return path - -def request_port(request): - host = request.host - i = host.find(':') - if i >= 0: - port = host[i+1:] - try: - int(port) - except ValueError: - _debug("nonnumeric port: '%s'", port) - return None - else: - port = DEFAULT_HTTP_PORT - return port - -# Characters in addition to A-Z, a-z, 0-9, '_', '.', and '-' that don't -# need to be escaped to form a valid HTTP URL (RFCs 2396 and 1738). -HTTP_PATH_SAFE = "%/;:@&=+$,!~*'()" -ESCAPED_CHAR_RE = re.compile(r"%([0-9a-fA-F][0-9a-fA-F])") -def uppercase_escaped_char(match): - return "%%%s" % match.group(1).upper() -def escape_path(path): - """Escape any invalid characters in HTTP URL, and uppercase all escapes.""" - # There's no knowing what character encoding was used to create URLs - # containing %-escapes, but since we have to pick one to escape invalid - # path characters, we pick UTF-8, as recommended in the HTML 4.0 - # specification: - # http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.2.1 - # And here, kind of: draft-fielding-uri-rfc2396bis-03 - # (And in draft IRI specification: draft-duerst-iri-05) - # (And here, for new URI schemes: RFC 2718) - path = quote(path, HTTP_PATH_SAFE) - path = ESCAPED_CHAR_RE.sub(uppercase_escaped_char, path) - return path - -def reach(h): - """Return reach of host h, as defined by RFC 2965, section 1. - - The reach R of a host name H is defined as follows: - - * If - - - H is the host domain name of a host; and, - - - H has the form A.B; and - - - A has no embedded (that is, interior) dots; and - - - B has at least one embedded dot, or B is the string "local". - then the reach of H is .B. - - * Otherwise, the reach of H is H. - - >>> reach("www.acme.com") - '.acme.com' - >>> reach("acme.com") - 'acme.com' - >>> reach("acme.local") - '.local' - - """ - i = h.find(".") - if i >= 0: - #a = h[:i] # this line is only here to show what a is - b = h[i+1:] - i = b.find(".") - if is_HDN(h) and (i >= 0 or b == "local"): - return "."+b - return h - -def is_third_party(request): - """ - - RFC 2965, section 3.3.6: - - An unverifiable transaction is to a third-party host if its request- - host U does not domain-match the reach R of the request-host O in the - origin transaction. - - """ - req_host = request_host(request) - if not domain_match(req_host, reach(request.get_origin_req_host())): - return True - else: - return False - - -class Cookie(object): - """HTTP Cookie. - - This class represents both Netscape and RFC 2965 cookies. - - This is deliberately a very simple class. It just holds attributes. It's - possible to construct Cookie instances that don't comply with the cookie - standards. CookieJar.make_cookies is the factory function for Cookie - objects -- it deals with cookie parsing, supplying defaults, and - normalising to the representation used in this class. CookiePolicy is - responsible for checking them to see whether they should be accepted from - and returned to the server. - - Note that the port may be present in the headers, but unspecified ("Port" - rather than"Port=80", for example); if this is the case, port is None. - - """ - - def __init__(self, version, name, value, - port, port_specified, - domain, domain_specified, domain_initial_dot, - path, path_specified, - secure, - expires, - discard, - comment, - comment_url, - rest, - rfc2109=False, - ): - - if version is not None: version = int(version) - if expires is not None: expires = int(expires) - if port is None and port_specified is True: - raise ValueError("if port is None, port_specified must be false") - - self.version = version - self.name = name - self.value = value - self.port = port - self.port_specified = port_specified - # normalise case, as per RFC 2965 section 3.3.3 - self.domain = domain.lower() - self.domain_specified = domain_specified - # Sigh. We need to know whether the domain given in the - # cookie-attribute had an initial dot, in order to follow RFC 2965 - # (as clarified in draft errata). Needed for the returned $Domain - # value. - self.domain_initial_dot = domain_initial_dot - self.path = path - self.path_specified = path_specified - self.secure = secure - self.expires = expires - self.discard = discard - self.comment = comment - self.comment_url = comment_url - self.rfc2109 = rfc2109 - - self._rest = copy.copy(rest) - - def has_nonstandard_attr(self, name): - return name in self._rest - def get_nonstandard_attr(self, name, default=None): - return self._rest.get(name, default) - def set_nonstandard_attr(self, name, value): - self._rest[name] = value - - def is_expired(self, now=None): - if now is None: now = time.time() - if (self.expires is not None) and (self.expires <= now): - return True - return False - - def __str__(self): - if self.port is None: p = "" - else: p = ":"+self.port - limit = self.domain + p + self.path - if self.value is not None: - namevalue = "%s=%s" % (self.name, self.value) - else: - namevalue = self.name - return "" % (namevalue, limit) - - @as_native_str() - def __repr__(self): - args = [] - for name in ("version", "name", "value", - "port", "port_specified", - "domain", "domain_specified", "domain_initial_dot", - "path", "path_specified", - "secure", "expires", "discard", "comment", "comment_url", - ): - attr = getattr(self, name) - ### Python-Future: - # Avoid u'...' prefixes for unicode strings: - if isinstance(attr, str): - attr = str(attr) - ### - args.append(str("%s=%s") % (name, repr(attr))) - args.append("rest=%s" % repr(self._rest)) - args.append("rfc2109=%s" % repr(self.rfc2109)) - return "Cookie(%s)" % ", ".join(args) - - -class CookiePolicy(object): - """Defines which cookies get accepted from and returned to server. - - May also modify cookies, though this is probably a bad idea. - - The subclass DefaultCookiePolicy defines the standard rules for Netscape - and RFC 2965 cookies -- override that if you want a customised policy. - - """ - def set_ok(self, cookie, request): - """Return true if (and only if) cookie should be accepted from server. - - Currently, pre-expired cookies never get this far -- the CookieJar - class deletes such cookies itself. - - """ - raise NotImplementedError() - - def return_ok(self, cookie, request): - """Return true if (and only if) cookie should be returned to server.""" - raise NotImplementedError() - - def domain_return_ok(self, domain, request): - """Return false if cookies should not be returned, given cookie domain. - """ - return True - - def path_return_ok(self, path, request): - """Return false if cookies should not be returned, given cookie path. - """ - return True - - -class DefaultCookiePolicy(CookiePolicy): - """Implements the standard rules for accepting and returning cookies.""" - - DomainStrictNoDots = 1 - DomainStrictNonDomain = 2 - DomainRFC2965Match = 4 - - DomainLiberal = 0 - DomainStrict = DomainStrictNoDots|DomainStrictNonDomain - - def __init__(self, - blocked_domains=None, allowed_domains=None, - netscape=True, rfc2965=False, - rfc2109_as_netscape=None, - hide_cookie2=False, - strict_domain=False, - strict_rfc2965_unverifiable=True, - strict_ns_unverifiable=False, - strict_ns_domain=DomainLiberal, - strict_ns_set_initial_dollar=False, - strict_ns_set_path=False, - ): - """Constructor arguments should be passed as keyword arguments only.""" - self.netscape = netscape - self.rfc2965 = rfc2965 - self.rfc2109_as_netscape = rfc2109_as_netscape - self.hide_cookie2 = hide_cookie2 - self.strict_domain = strict_domain - self.strict_rfc2965_unverifiable = strict_rfc2965_unverifiable - self.strict_ns_unverifiable = strict_ns_unverifiable - self.strict_ns_domain = strict_ns_domain - self.strict_ns_set_initial_dollar = strict_ns_set_initial_dollar - self.strict_ns_set_path = strict_ns_set_path - - if blocked_domains is not None: - self._blocked_domains = tuple(blocked_domains) - else: - self._blocked_domains = () - - if allowed_domains is not None: - allowed_domains = tuple(allowed_domains) - self._allowed_domains = allowed_domains - - def blocked_domains(self): - """Return the sequence of blocked domains (as a tuple).""" - return self._blocked_domains - def set_blocked_domains(self, blocked_domains): - """Set the sequence of blocked domains.""" - self._blocked_domains = tuple(blocked_domains) - - def is_blocked(self, domain): - for blocked_domain in self._blocked_domains: - if user_domain_match(domain, blocked_domain): - return True - return False - - def allowed_domains(self): - """Return None, or the sequence of allowed domains (as a tuple).""" - return self._allowed_domains - def set_allowed_domains(self, allowed_domains): - """Set the sequence of allowed domains, or None.""" - if allowed_domains is not None: - allowed_domains = tuple(allowed_domains) - self._allowed_domains = allowed_domains - - def is_not_allowed(self, domain): - if self._allowed_domains is None: - return False - for allowed_domain in self._allowed_domains: - if user_domain_match(domain, allowed_domain): - return False - return True - - def set_ok(self, cookie, request): - """ - If you override .set_ok(), be sure to call this method. If it returns - false, so should your subclass (assuming your subclass wants to be more - strict about which cookies to accept). - - """ - _debug(" - checking cookie %s=%s", cookie.name, cookie.value) - - assert cookie.name is not None - - for n in "version", "verifiability", "name", "path", "domain", "port": - fn_name = "set_ok_"+n - fn = getattr(self, fn_name) - if not fn(cookie, request): - return False - - return True - - def set_ok_version(self, cookie, request): - if cookie.version is None: - # Version is always set to 0 by parse_ns_headers if it's a Netscape - # cookie, so this must be an invalid RFC 2965 cookie. - _debug(" Set-Cookie2 without version attribute (%s=%s)", - cookie.name, cookie.value) - return False - if cookie.version > 0 and not self.rfc2965: - _debug(" RFC 2965 cookies are switched off") - return False - elif cookie.version == 0 and not self.netscape: - _debug(" Netscape cookies are switched off") - return False - return True - - def set_ok_verifiability(self, cookie, request): - if request.unverifiable and is_third_party(request): - if cookie.version > 0 and self.strict_rfc2965_unverifiable: - _debug(" third-party RFC 2965 cookie during " - "unverifiable transaction") - return False - elif cookie.version == 0 and self.strict_ns_unverifiable: - _debug(" third-party Netscape cookie during " - "unverifiable transaction") - return False - return True - - def set_ok_name(self, cookie, request): - # Try and stop servers setting V0 cookies designed to hack other - # servers that know both V0 and V1 protocols. - if (cookie.version == 0 and self.strict_ns_set_initial_dollar and - cookie.name.startswith("$")): - _debug(" illegal name (starts with '$'): '%s'", cookie.name) - return False - return True - - def set_ok_path(self, cookie, request): - if cookie.path_specified: - req_path = request_path(request) - if ((cookie.version > 0 or - (cookie.version == 0 and self.strict_ns_set_path)) and - not req_path.startswith(cookie.path)): - _debug(" path attribute %s is not a prefix of request " - "path %s", cookie.path, req_path) - return False - return True - - def set_ok_domain(self, cookie, request): - if self.is_blocked(cookie.domain): - _debug(" domain %s is in user block-list", cookie.domain) - return False - if self.is_not_allowed(cookie.domain): - _debug(" domain %s is not in user allow-list", cookie.domain) - return False - if cookie.domain_specified: - req_host, erhn = eff_request_host(request) - domain = cookie.domain - if self.strict_domain and (domain.count(".") >= 2): - # XXX This should probably be compared with the Konqueror - # (kcookiejar.cpp) and Mozilla implementations, but it's a - # losing battle. - i = domain.rfind(".") - j = domain.rfind(".", 0, i) - if j == 0: # domain like .foo.bar - tld = domain[i+1:] - sld = domain[j+1:i] - if sld.lower() in ("co", "ac", "com", "edu", "org", "net", - "gov", "mil", "int", "aero", "biz", "cat", "coop", - "info", "jobs", "mobi", "museum", "name", "pro", - "travel", "eu") and len(tld) == 2: - # domain like .co.uk - _debug(" country-code second level domain %s", domain) - return False - if domain.startswith("."): - undotted_domain = domain[1:] - else: - undotted_domain = domain - embedded_dots = (undotted_domain.find(".") >= 0) - if not embedded_dots and domain != ".local": - _debug(" non-local domain %s contains no embedded dot", - domain) - return False - if cookie.version == 0: - if (not erhn.endswith(domain) and - (not erhn.startswith(".") and - not ("."+erhn).endswith(domain))): - _debug(" effective request-host %s (even with added " - "initial dot) does not end with %s", - erhn, domain) - return False - if (cookie.version > 0 or - (self.strict_ns_domain & self.DomainRFC2965Match)): - if not domain_match(erhn, domain): - _debug(" effective request-host %s does not domain-match " - "%s", erhn, domain) - return False - if (cookie.version > 0 or - (self.strict_ns_domain & self.DomainStrictNoDots)): - host_prefix = req_host[:-len(domain)] - if (host_prefix.find(".") >= 0 and - not IPV4_RE.search(req_host)): - _debug(" host prefix %s for domain %s contains a dot", - host_prefix, domain) - return False - return True - - def set_ok_port(self, cookie, request): - if cookie.port_specified: - req_port = request_port(request) - if req_port is None: - req_port = "80" - else: - req_port = str(req_port) - for p in cookie.port.split(","): - try: - int(p) - except ValueError: - _debug(" bad port %s (not numeric)", p) - return False - if p == req_port: - break - else: - _debug(" request port (%s) not found in %s", - req_port, cookie.port) - return False - return True - - def return_ok(self, cookie, request): - """ - If you override .return_ok(), be sure to call this method. If it - returns false, so should your subclass (assuming your subclass wants to - be more strict about which cookies to return). - - """ - # Path has already been checked by .path_return_ok(), and domain - # blocking done by .domain_return_ok(). - _debug(" - checking cookie %s=%s", cookie.name, cookie.value) - - for n in "version", "verifiability", "secure", "expires", "port", "domain": - fn_name = "return_ok_"+n - fn = getattr(self, fn_name) - if not fn(cookie, request): - return False - return True - - def return_ok_version(self, cookie, request): - if cookie.version > 0 and not self.rfc2965: - _debug(" RFC 2965 cookies are switched off") - return False - elif cookie.version == 0 and not self.netscape: - _debug(" Netscape cookies are switched off") - return False - return True - - def return_ok_verifiability(self, cookie, request): - if request.unverifiable and is_third_party(request): - if cookie.version > 0 and self.strict_rfc2965_unverifiable: - _debug(" third-party RFC 2965 cookie during unverifiable " - "transaction") - return False - elif cookie.version == 0 and self.strict_ns_unverifiable: - _debug(" third-party Netscape cookie during unverifiable " - "transaction") - return False - return True - - def return_ok_secure(self, cookie, request): - if cookie.secure and request.type != "https": - _debug(" secure cookie with non-secure request") - return False - return True - - def return_ok_expires(self, cookie, request): - if cookie.is_expired(self._now): - _debug(" cookie expired") - return False - return True - - def return_ok_port(self, cookie, request): - if cookie.port: - req_port = request_port(request) - if req_port is None: - req_port = "80" - for p in cookie.port.split(","): - if p == req_port: - break - else: - _debug(" request port %s does not match cookie port %s", - req_port, cookie.port) - return False - return True - - def return_ok_domain(self, cookie, request): - req_host, erhn = eff_request_host(request) - domain = cookie.domain - - # strict check of non-domain cookies: Mozilla does this, MSIE5 doesn't - if (cookie.version == 0 and - (self.strict_ns_domain & self.DomainStrictNonDomain) and - not cookie.domain_specified and domain != erhn): - _debug(" cookie with unspecified domain does not string-compare " - "equal to request domain") - return False - - if cookie.version > 0 and not domain_match(erhn, domain): - _debug(" effective request-host name %s does not domain-match " - "RFC 2965 cookie domain %s", erhn, domain) - return False - if cookie.version == 0 and not ("."+erhn).endswith(domain): - _debug(" request-host %s does not match Netscape cookie domain " - "%s", req_host, domain) - return False - return True - - def domain_return_ok(self, domain, request): - # Liberal check of. This is here as an optimization to avoid - # having to load lots of MSIE cookie files unless necessary. - req_host, erhn = eff_request_host(request) - if not req_host.startswith("."): - req_host = "."+req_host - if not erhn.startswith("."): - erhn = "."+erhn - if not (req_host.endswith(domain) or erhn.endswith(domain)): - #_debug(" request domain %s does not match cookie domain %s", - # req_host, domain) - return False - - if self.is_blocked(domain): - _debug(" domain %s is in user block-list", domain) - return False - if self.is_not_allowed(domain): - _debug(" domain %s is not in user allow-list", domain) - return False - - return True - - def path_return_ok(self, path, request): - _debug("- checking cookie path=%s", path) - req_path = request_path(request) - if not req_path.startswith(path): - _debug(" %s does not path-match %s", req_path, path) - return False - return True - - -def vals_sorted_by_key(adict): - keys = sorted(adict.keys()) - return map(adict.get, keys) - -def deepvalues(mapping): - """Iterates over nested mapping, depth-first, in sorted order by key.""" - values = vals_sorted_by_key(mapping) - for obj in values: - mapping = False - try: - obj.items - except AttributeError: - pass - else: - mapping = True - for subobj in deepvalues(obj): - yield subobj - if not mapping: - yield obj - - -# Used as second parameter to dict.get() method, to distinguish absent -# dict key from one with a None value. -class Absent(object): pass - -class CookieJar(object): - """Collection of HTTP cookies. - - You may not need to know about this class: try - urllib.request.build_opener(HTTPCookieProcessor).open(url). - """ - - non_word_re = re.compile(r"\W") - quote_re = re.compile(r"([\"\\])") - strict_domain_re = re.compile(r"\.?[^.]*") - domain_re = re.compile(r"[^.]*") - dots_re = re.compile(r"^\.+") - - magic_re = re.compile(r"^\#LWP-Cookies-(\d+\.\d+)", re.ASCII) - - def __init__(self, policy=None): - if policy is None: - policy = DefaultCookiePolicy() - self._policy = policy - - self._cookies_lock = _threading.RLock() - self._cookies = {} - - def set_policy(self, policy): - self._policy = policy - - def _cookies_for_domain(self, domain, request): - cookies = [] - if not self._policy.domain_return_ok(domain, request): - return [] - _debug("Checking %s for cookies to return", domain) - cookies_by_path = self._cookies[domain] - for path in cookies_by_path.keys(): - if not self._policy.path_return_ok(path, request): - continue - cookies_by_name = cookies_by_path[path] - for cookie in cookies_by_name.values(): - if not self._policy.return_ok(cookie, request): - _debug(" not returning cookie") - continue - _debug(" it's a match") - cookies.append(cookie) - return cookies - - def _cookies_for_request(self, request): - """Return a list of cookies to be returned to server.""" - cookies = [] - for domain in self._cookies.keys(): - cookies.extend(self._cookies_for_domain(domain, request)) - return cookies - - def _cookie_attrs(self, cookies): - """Return a list of cookie-attributes to be returned to server. - - like ['foo="bar"; $Path="/"', ...] - - The $Version attribute is also added when appropriate (currently only - once per request). - - """ - # add cookies in order of most specific (ie. longest) path first - cookies.sort(key=lambda a: len(a.path), reverse=True) - - version_set = False - - attrs = [] - for cookie in cookies: - # set version of Cookie header - # XXX - # What should it be if multiple matching Set-Cookie headers have - # different versions themselves? - # Answer: there is no answer; was supposed to be settled by - # RFC 2965 errata, but that may never appear... - version = cookie.version - if not version_set: - version_set = True - if version > 0: - attrs.append("$Version=%s" % version) - - # quote cookie value if necessary - # (not for Netscape protocol, which already has any quotes - # intact, due to the poorly-specified Netscape Cookie: syntax) - if ((cookie.value is not None) and - self.non_word_re.search(cookie.value) and version > 0): - value = self.quote_re.sub(r"\\\1", cookie.value) - else: - value = cookie.value - - # add cookie-attributes to be returned in Cookie header - if cookie.value is None: - attrs.append(cookie.name) - else: - attrs.append("%s=%s" % (cookie.name, value)) - if version > 0: - if cookie.path_specified: - attrs.append('$Path="%s"' % cookie.path) - if cookie.domain.startswith("."): - domain = cookie.domain - if (not cookie.domain_initial_dot and - domain.startswith(".")): - domain = domain[1:] - attrs.append('$Domain="%s"' % domain) - if cookie.port is not None: - p = "$Port" - if cookie.port_specified: - p = p + ('="%s"' % cookie.port) - attrs.append(p) - - return attrs - - def add_cookie_header(self, request): - """Add correct Cookie: header to request (urllib.request.Request object). - - The Cookie2 header is also added unless policy.hide_cookie2 is true. - - """ - _debug("add_cookie_header") - self._cookies_lock.acquire() - try: - - self._policy._now = self._now = int(time.time()) - - cookies = self._cookies_for_request(request) - - attrs = self._cookie_attrs(cookies) - if attrs: - if not request.has_header("Cookie"): - request.add_unredirected_header( - "Cookie", "; ".join(attrs)) - - # if necessary, advertise that we know RFC 2965 - if (self._policy.rfc2965 and not self._policy.hide_cookie2 and - not request.has_header("Cookie2")): - for cookie in cookies: - if cookie.version != 1: - request.add_unredirected_header("Cookie2", '$Version="1"') - break - - finally: - self._cookies_lock.release() - - self.clear_expired_cookies() - - def _normalized_cookie_tuples(self, attrs_set): - """Return list of tuples containing normalised cookie information. - - attrs_set is the list of lists of key,value pairs extracted from - the Set-Cookie or Set-Cookie2 headers. - - Tuples are name, value, standard, rest, where name and value are the - cookie name and value, standard is a dictionary containing the standard - cookie-attributes (discard, secure, version, expires or max-age, - domain, path and port) and rest is a dictionary containing the rest of - the cookie-attributes. - - """ - cookie_tuples = [] - - boolean_attrs = "discard", "secure" - value_attrs = ("version", - "expires", "max-age", - "domain", "path", "port", - "comment", "commenturl") - - for cookie_attrs in attrs_set: - name, value = cookie_attrs[0] - - # Build dictionary of standard cookie-attributes (standard) and - # dictionary of other cookie-attributes (rest). - - # Note: expiry time is normalised to seconds since epoch. V0 - # cookies should have the Expires cookie-attribute, and V1 cookies - # should have Max-Age, but since V1 includes RFC 2109 cookies (and - # since V0 cookies may be a mish-mash of Netscape and RFC 2109), we - # accept either (but prefer Max-Age). - max_age_set = False - - bad_cookie = False - - standard = {} - rest = {} - for k, v in cookie_attrs[1:]: - lc = k.lower() - # don't lose case distinction for unknown fields - if lc in value_attrs or lc in boolean_attrs: - k = lc - if k in boolean_attrs and v is None: - # boolean cookie-attribute is present, but has no value - # (like "discard", rather than "port=80") - v = True - if k in standard: - # only first value is significant - continue - if k == "domain": - if v is None: - _debug(" missing value for domain attribute") - bad_cookie = True - break - # RFC 2965 section 3.3.3 - v = v.lower() - if k == "expires": - if max_age_set: - # Prefer max-age to expires (like Mozilla) - continue - if v is None: - _debug(" missing or invalid value for expires " - "attribute: treating as session cookie") - continue - if k == "max-age": - max_age_set = True - try: - v = int(v) - except ValueError: - _debug(" missing or invalid (non-numeric) value for " - "max-age attribute") - bad_cookie = True - break - # convert RFC 2965 Max-Age to seconds since epoch - # XXX Strictly you're supposed to follow RFC 2616 - # age-calculation rules. Remember that zero Max-Age is a - # is a request to discard (old and new) cookie, though. - k = "expires" - v = self._now + v - if (k in value_attrs) or (k in boolean_attrs): - if (v is None and - k not in ("port", "comment", "commenturl")): - _debug(" missing value for %s attribute" % k) - bad_cookie = True - break - standard[k] = v - else: - rest[k] = v - - if bad_cookie: - continue - - cookie_tuples.append((name, value, standard, rest)) - - return cookie_tuples - - def _cookie_from_cookie_tuple(self, tup, request): - # standard is dict of standard cookie-attributes, rest is dict of the - # rest of them - name, value, standard, rest = tup - - domain = standard.get("domain", Absent) - path = standard.get("path", Absent) - port = standard.get("port", Absent) - expires = standard.get("expires", Absent) - - # set the easy defaults - version = standard.get("version", None) - if version is not None: - try: - version = int(version) - except ValueError: - return None # invalid version, ignore cookie - secure = standard.get("secure", False) - # (discard is also set if expires is Absent) - discard = standard.get("discard", False) - comment = standard.get("comment", None) - comment_url = standard.get("commenturl", None) - - # set default path - if path is not Absent and path != "": - path_specified = True - path = escape_path(path) - else: - path_specified = False - path = request_path(request) - i = path.rfind("/") - if i != -1: - if version == 0: - # Netscape spec parts company from reality here - path = path[:i] - else: - path = path[:i+1] - if len(path) == 0: path = "/" - - # set default domain - domain_specified = domain is not Absent - # but first we have to remember whether it starts with a dot - domain_initial_dot = False - if domain_specified: - domain_initial_dot = bool(domain.startswith(".")) - if domain is Absent: - req_host, erhn = eff_request_host(request) - domain = erhn - elif not domain.startswith("."): - domain = "."+domain - - # set default port - port_specified = False - if port is not Absent: - if port is None: - # Port attr present, but has no value: default to request port. - # Cookie should then only be sent back on that port. - port = request_port(request) - else: - port_specified = True - port = re.sub(r"\s+", "", port) - else: - # No port attr present. Cookie can be sent back on any port. - port = None - - # set default expires and discard - if expires is Absent: - expires = None - discard = True - elif expires <= self._now: - # Expiry date in past is request to delete cookie. This can't be - # in DefaultCookiePolicy, because can't delete cookies there. - try: - self.clear(domain, path, name) - except KeyError: - pass - _debug("Expiring cookie, domain='%s', path='%s', name='%s'", - domain, path, name) - return None - - return Cookie(version, - name, value, - port, port_specified, - domain, domain_specified, domain_initial_dot, - path, path_specified, - secure, - expires, - discard, - comment, - comment_url, - rest) - - def _cookies_from_attrs_set(self, attrs_set, request): - cookie_tuples = self._normalized_cookie_tuples(attrs_set) - - cookies = [] - for tup in cookie_tuples: - cookie = self._cookie_from_cookie_tuple(tup, request) - if cookie: cookies.append(cookie) - return cookies - - def _process_rfc2109_cookies(self, cookies): - rfc2109_as_ns = getattr(self._policy, 'rfc2109_as_netscape', None) - if rfc2109_as_ns is None: - rfc2109_as_ns = not self._policy.rfc2965 - for cookie in cookies: - if cookie.version == 1: - cookie.rfc2109 = True - if rfc2109_as_ns: - # treat 2109 cookies as Netscape cookies rather than - # as RFC2965 cookies - cookie.version = 0 - - def make_cookies(self, response, request): - """Return sequence of Cookie objects extracted from response object.""" - # get cookie-attributes for RFC 2965 and Netscape protocols - headers = response.info() - rfc2965_hdrs = headers.get_all("Set-Cookie2", []) - ns_hdrs = headers.get_all("Set-Cookie", []) - - rfc2965 = self._policy.rfc2965 - netscape = self._policy.netscape - - if ((not rfc2965_hdrs and not ns_hdrs) or - (not ns_hdrs and not rfc2965) or - (not rfc2965_hdrs and not netscape) or - (not netscape and not rfc2965)): - return [] # no relevant cookie headers: quick exit - - try: - cookies = self._cookies_from_attrs_set( - split_header_words(rfc2965_hdrs), request) - except Exception: - _warn_unhandled_exception() - cookies = [] - - if ns_hdrs and netscape: - try: - # RFC 2109 and Netscape cookies - ns_cookies = self._cookies_from_attrs_set( - parse_ns_headers(ns_hdrs), request) - except Exception: - _warn_unhandled_exception() - ns_cookies = [] - self._process_rfc2109_cookies(ns_cookies) - - # Look for Netscape cookies (from Set-Cookie headers) that match - # corresponding RFC 2965 cookies (from Set-Cookie2 headers). - # For each match, keep the RFC 2965 cookie and ignore the Netscape - # cookie (RFC 2965 section 9.1). Actually, RFC 2109 cookies are - # bundled in with the Netscape cookies for this purpose, which is - # reasonable behaviour. - if rfc2965: - lookup = {} - for cookie in cookies: - lookup[(cookie.domain, cookie.path, cookie.name)] = None - - def no_matching_rfc2965(ns_cookie, lookup=lookup): - key = ns_cookie.domain, ns_cookie.path, ns_cookie.name - return key not in lookup - ns_cookies = filter(no_matching_rfc2965, ns_cookies) - - if ns_cookies: - cookies.extend(ns_cookies) - - return cookies - - def set_cookie_if_ok(self, cookie, request): - """Set a cookie if policy says it's OK to do so.""" - self._cookies_lock.acquire() - try: - self._policy._now = self._now = int(time.time()) - - if self._policy.set_ok(cookie, request): - self.set_cookie(cookie) - - - finally: - self._cookies_lock.release() - - def set_cookie(self, cookie): - """Set a cookie, without checking whether or not it should be set.""" - c = self._cookies - self._cookies_lock.acquire() - try: - if cookie.domain not in c: c[cookie.domain] = {} - c2 = c[cookie.domain] - if cookie.path not in c2: c2[cookie.path] = {} - c3 = c2[cookie.path] - c3[cookie.name] = cookie - finally: - self._cookies_lock.release() - - def extract_cookies(self, response, request): - """Extract cookies from response, where allowable given the request.""" - _debug("extract_cookies: %s", response.info()) - self._cookies_lock.acquire() - try: - self._policy._now = self._now = int(time.time()) - - for cookie in self.make_cookies(response, request): - if self._policy.set_ok(cookie, request): - _debug(" setting cookie: %s", cookie) - self.set_cookie(cookie) - finally: - self._cookies_lock.release() - - def clear(self, domain=None, path=None, name=None): - """Clear some cookies. - - Invoking this method without arguments will clear all cookies. If - given a single argument, only cookies belonging to that domain will be - removed. If given two arguments, cookies belonging to the specified - path within that domain are removed. If given three arguments, then - the cookie with the specified name, path and domain is removed. - - Raises KeyError if no matching cookie exists. - - """ - if name is not None: - if (domain is None) or (path is None): - raise ValueError( - "domain and path must be given to remove a cookie by name") - del self._cookies[domain][path][name] - elif path is not None: - if domain is None: - raise ValueError( - "domain must be given to remove cookies by path") - del self._cookies[domain][path] - elif domain is not None: - del self._cookies[domain] - else: - self._cookies = {} - - def clear_session_cookies(self): - """Discard all session cookies. - - Note that the .save() method won't save session cookies anyway, unless - you ask otherwise by passing a true ignore_discard argument. - - """ - self._cookies_lock.acquire() - try: - for cookie in self: - if cookie.discard: - self.clear(cookie.domain, cookie.path, cookie.name) - finally: - self._cookies_lock.release() - - def clear_expired_cookies(self): - """Discard all expired cookies. - - You probably don't need to call this method: expired cookies are never - sent back to the server (provided you're using DefaultCookiePolicy), - this method is called by CookieJar itself every so often, and the - .save() method won't save expired cookies anyway (unless you ask - otherwise by passing a true ignore_expires argument). - - """ - self._cookies_lock.acquire() - try: - now = time.time() - for cookie in self: - if cookie.is_expired(now): - self.clear(cookie.domain, cookie.path, cookie.name) - finally: - self._cookies_lock.release() - - def __iter__(self): - return deepvalues(self._cookies) - - def __len__(self): - """Return number of contained cookies.""" - i = 0 - for cookie in self: i = i + 1 - return i - - @as_native_str() - def __repr__(self): - r = [] - for cookie in self: r.append(repr(cookie)) - return "<%s[%s]>" % (self.__class__, ", ".join(r)) - - def __str__(self): - r = [] - for cookie in self: r.append(str(cookie)) - return "<%s[%s]>" % (self.__class__, ", ".join(r)) - - -# derives from IOError for backwards-compatibility with Python 2.4.0 -class LoadError(IOError): pass - -class FileCookieJar(CookieJar): - """CookieJar that can be loaded from and saved to a file.""" - - def __init__(self, filename=None, delayload=False, policy=None): - """ - Cookies are NOT loaded from the named file until either the .load() or - .revert() method is called. - - """ - CookieJar.__init__(self, policy) - if filename is not None: - try: - filename+"" - except: - raise ValueError("filename must be string-like") - self.filename = filename - self.delayload = bool(delayload) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - """Save cookies to a file.""" - raise NotImplementedError() - - def load(self, filename=None, ignore_discard=False, ignore_expires=False): - """Load cookies from a file.""" - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename) - try: - self._really_load(f, filename, ignore_discard, ignore_expires) - finally: - f.close() - - def revert(self, filename=None, - ignore_discard=False, ignore_expires=False): - """Clear all cookies and reload cookies from a saved file. - - Raises LoadError (or IOError) if reversion is not successful; the - object's state will not be altered if this happens. - - """ - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - self._cookies_lock.acquire() - try: - - old_state = copy.deepcopy(self._cookies) - self._cookies = {} - try: - self.load(filename, ignore_discard, ignore_expires) - except (LoadError, IOError): - self._cookies = old_state - raise - - finally: - self._cookies_lock.release() - - -def lwp_cookie_str(cookie): - """Return string representation of Cookie in an the LWP cookie file format. - - Actually, the format is extended a bit -- see module docstring. - - """ - h = [(cookie.name, cookie.value), - ("path", cookie.path), - ("domain", cookie.domain)] - if cookie.port is not None: h.append(("port", cookie.port)) - if cookie.path_specified: h.append(("path_spec", None)) - if cookie.port_specified: h.append(("port_spec", None)) - if cookie.domain_initial_dot: h.append(("domain_dot", None)) - if cookie.secure: h.append(("secure", None)) - if cookie.expires: h.append(("expires", - time2isoz(float(cookie.expires)))) - if cookie.discard: h.append(("discard", None)) - if cookie.comment: h.append(("comment", cookie.comment)) - if cookie.comment_url: h.append(("commenturl", cookie.comment_url)) - - keys = sorted(cookie._rest.keys()) - for k in keys: - h.append((k, str(cookie._rest[k]))) - - h.append(("version", str(cookie.version))) - - return join_header_words([h]) - -class LWPCookieJar(FileCookieJar): - """ - The LWPCookieJar saves a sequence of "Set-Cookie3" lines. - "Set-Cookie3" is the format used by the libwww-perl libary, not known - to be compatible with any browser, but which is easy to read and - doesn't lose information about RFC 2965 cookies. - - Additional methods - - as_lwp_str(ignore_discard=True, ignore_expired=True) - - """ - - def as_lwp_str(self, ignore_discard=True, ignore_expires=True): - """Return cookies as a string of "\\n"-separated "Set-Cookie3" headers. - - ignore_discard and ignore_expires: see docstring for FileCookieJar.save - - """ - now = time.time() - r = [] - for cookie in self: - if not ignore_discard and cookie.discard: - continue - if not ignore_expires and cookie.is_expired(now): - continue - r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie)) - return "\n".join(r+[""]) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename, "w") - try: - # There really isn't an LWP Cookies 2.0 format, but this indicates - # that there is extra information in here (domain_dot and - # port_spec) while still being compatible with libwww-perl, I hope. - f.write("#LWP-Cookies-2.0\n") - f.write(self.as_lwp_str(ignore_discard, ignore_expires)) - finally: - f.close() - - def _really_load(self, f, filename, ignore_discard, ignore_expires): - magic = f.readline() - if not self.magic_re.search(magic): - msg = ("%r does not look like a Set-Cookie3 (LWP) format " - "file" % filename) - raise LoadError(msg) - - now = time.time() - - header = "Set-Cookie3:" - boolean_attrs = ("port_spec", "path_spec", "domain_dot", - "secure", "discard") - value_attrs = ("version", - "port", "path", "domain", - "expires", - "comment", "commenturl") - - try: - while 1: - line = f.readline() - if line == "": break - if not line.startswith(header): - continue - line = line[len(header):].strip() - - for data in split_header_words([line]): - name, value = data[0] - standard = {} - rest = {} - for k in boolean_attrs: - standard[k] = False - for k, v in data[1:]: - if k is not None: - lc = k.lower() - else: - lc = None - # don't lose case distinction for unknown fields - if (lc in value_attrs) or (lc in boolean_attrs): - k = lc - if k in boolean_attrs: - if v is None: v = True - standard[k] = v - elif k in value_attrs: - standard[k] = v - else: - rest[k] = v - - h = standard.get - expires = h("expires") - discard = h("discard") - if expires is not None: - expires = iso2time(expires) - if expires is None: - discard = True - domain = h("domain") - domain_specified = domain.startswith(".") - c = Cookie(h("version"), name, value, - h("port"), h("port_spec"), - domain, domain_specified, h("domain_dot"), - h("path"), h("path_spec"), - h("secure"), - expires, - discard, - h("comment"), - h("commenturl"), - rest) - if not ignore_discard and c.discard: - continue - if not ignore_expires and c.is_expired(now): - continue - self.set_cookie(c) - - except IOError: - raise - except Exception: - _warn_unhandled_exception() - raise LoadError("invalid Set-Cookie3 format file %r: %r" % - (filename, line)) - - -class MozillaCookieJar(FileCookieJar): - """ - - WARNING: you may want to backup your browser's cookies file if you use - this class to save cookies. I *think* it works, but there have been - bugs in the past! - - This class differs from CookieJar only in the format it uses to save and - load cookies to and from a file. This class uses the Mozilla/Netscape - `cookies.txt' format. lynx uses this file format, too. - - Don't expect cookies saved while the browser is running to be noticed by - the browser (in fact, Mozilla on unix will overwrite your saved cookies if - you change them on disk while it's running; on Windows, you probably can't - save at all while the browser is running). - - Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to - Netscape cookies on saving. - - In particular, the cookie version and port number information is lost, - together with information about whether or not Path, Port and Discard were - specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the - domain as set in the HTTP header started with a dot (yes, I'm aware some - domains in Netscape files start with a dot and some don't -- trust me, you - really don't want to know any more about this). - - Note that though Mozilla and Netscape use the same format, they use - slightly different headers. The class saves cookies using the Netscape - header by default (Mozilla can cope with that). - - """ - magic_re = re.compile("#( Netscape)? HTTP Cookie File") - header = """\ -# Netscape HTTP Cookie File -# http://www.netscape.com/newsref/std/cookie_spec.html -# This is a generated file! Do not edit. - -""" - - def _really_load(self, f, filename, ignore_discard, ignore_expires): - now = time.time() - - magic = f.readline() - if not self.magic_re.search(magic): - f.close() - raise LoadError( - "%r does not look like a Netscape format cookies file" % - filename) - - try: - while 1: - line = f.readline() - if line == "": break - - # last field may be absent, so keep any trailing tab - if line.endswith("\n"): line = line[:-1] - - # skip comments and blank lines XXX what is $ for? - if (line.strip().startswith(("#", "$")) or - line.strip() == ""): - continue - - domain, domain_specified, path, secure, expires, name, value = \ - line.split("\t") - secure = (secure == "TRUE") - domain_specified = (domain_specified == "TRUE") - if name == "": - # cookies.txt regards 'Set-Cookie: foo' as a cookie - # with no name, whereas http.cookiejar regards it as a - # cookie with no value. - name = value - value = None - - initial_dot = domain.startswith(".") - assert domain_specified == initial_dot - - discard = False - if expires == "": - expires = None - discard = True - - # assume path_specified is false - c = Cookie(0, name, value, - None, False, - domain, domain_specified, initial_dot, - path, False, - secure, - expires, - discard, - None, - None, - {}) - if not ignore_discard and c.discard: - continue - if not ignore_expires and c.is_expired(now): - continue - self.set_cookie(c) - - except IOError: - raise - except Exception: - _warn_unhandled_exception() - raise LoadError("invalid Netscape format cookies file %r: %r" % - (filename, line)) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename, "w") - try: - f.write(self.header) - now = time.time() - for cookie in self: - if not ignore_discard and cookie.discard: - continue - if not ignore_expires and cookie.is_expired(now): - continue - if cookie.secure: secure = "TRUE" - else: secure = "FALSE" - if cookie.domain.startswith("."): initial_dot = "TRUE" - else: initial_dot = "FALSE" - if cookie.expires is not None: - expires = str(cookie.expires) - else: - expires = "" - if cookie.value is None: - # cookies.txt regards 'Set-Cookie: foo' as a cookie - # with no name, whereas http.cookiejar regards it as a - # cookie with no value. - name = "" - value = cookie.name - else: - name = cookie.name - value = cookie.value - f.write( - "\t".join([cookie.domain, initial_dot, cookie.path, - secure, expires, name, value])+ - "\n") - finally: - f.close() diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/cookies.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/cookies.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/cookies.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/cookies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,597 +0,0 @@ -#### -# Copyright 2000 by Timothy O'Malley -# -# All Rights Reserved -# -# Permission to use, copy, modify, and distribute this software -# and its documentation for any purpose and without fee is hereby -# granted, provided that the above copyright notice appear in all -# copies and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Timothy O'Malley not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS -# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR -# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. -# -#### -# -# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp -# by Timothy O'Malley -# -# Cookie.py is a Python module for the handling of HTTP -# cookies as a Python dictionary. See RFC 2109 for more -# information on cookies. -# -# The original idea to treat Cookies as a dictionary came from -# Dave Mitchell (davem@magnet.com) in 1995, when he released the -# first version of nscookie.py. -# -#### - -r""" -http.cookies module ported to python-future from Py3.3 - -Here's a sample session to show how to use this module. -At the moment, this is the only documentation. - -The Basics ----------- - -Importing is easy... - - >>> from http import cookies - -Most of the time you start by creating a cookie. - - >>> C = cookies.SimpleCookie() - -Once you've created your Cookie, you can add values just as if it were -a dictionary. - - >>> C = cookies.SimpleCookie() - >>> C["fig"] = "newton" - >>> C["sugar"] = "wafer" - >>> C.output() - 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer' - -Notice that the printable representation of a Cookie is the -appropriate format for a Set-Cookie: header. This is the -default behavior. You can change the header and printed -attributes by using the .output() function - - >>> C = cookies.SimpleCookie() - >>> C["rocky"] = "road" - >>> C["rocky"]["path"] = "/cookie" - >>> print(C.output(header="Cookie:")) - Cookie: rocky=road; Path=/cookie - >>> print(C.output(attrs=[], header="Cookie:")) - Cookie: rocky=road - -The load() method of a Cookie extracts cookies from a string. In a -CGI script, you would use this method to extract the cookies from the -HTTP_COOKIE environment variable. - - >>> C = cookies.SimpleCookie() - >>> C.load("chips=ahoy; vienna=finger") - >>> C.output() - 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger' - -The load() method is darn-tootin smart about identifying cookies -within a string. Escaped quotation marks, nested semicolons, and other -such trickeries do not confuse it. - - >>> C = cookies.SimpleCookie() - >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') - >>> print(C) - Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" - -Each element of the Cookie also supports all of the RFC 2109 -Cookie attributes. Here's an example which sets the Path -attribute. - - >>> C = cookies.SimpleCookie() - >>> C["oreo"] = "doublestuff" - >>> C["oreo"]["path"] = "/" - >>> print(C) - Set-Cookie: oreo=doublestuff; Path=/ - -Each dictionary element has a 'value' attribute, which gives you -back the value associated with the key. - - >>> C = cookies.SimpleCookie() - >>> C["twix"] = "none for you" - >>> C["twix"].value - 'none for you' - -The SimpleCookie expects that all values should be standard strings. -Just to be sure, SimpleCookie invokes the str() builtin to convert -the value to a string, when the values are set dictionary-style. - - >>> C = cookies.SimpleCookie() - >>> C["number"] = 7 - >>> C["string"] = "seven" - >>> C["number"].value - '7' - >>> C["string"].value - 'seven' - >>> C.output() - 'Set-Cookie: number=7\r\nSet-Cookie: string=seven' - -Finis. -""" -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import chr, dict, int, str -from future.utils import PY2, as_native_str - -# -# Import our required modules -# -import re -re.ASCII = 0 # for py2 compatibility -import string - -__all__ = ["CookieError", "BaseCookie", "SimpleCookie"] - -_nulljoin = ''.join -_semispacejoin = '; '.join -_spacejoin = ' '.join - -# -# Define an exception visible to External modules -# -class CookieError(Exception): - pass - - -# These quoting routines conform to the RFC2109 specification, which in -# turn references the character definitions from RFC2068. They provide -# a two-way quoting algorithm. Any non-text character is translated -# into a 4 character sequence: a forward-slash followed by the -# three-digit octal equivalent of the character. Any '\' or '"' is -# quoted with a preceeding '\' slash. -# -# These are taken from RFC2068 and RFC2109. -# _LegalChars is the list of chars which don't require "'s -# _Translator hash-table for fast quoting -# -_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:" -_Translator = { - '\000' : '\\000', '\001' : '\\001', '\002' : '\\002', - '\003' : '\\003', '\004' : '\\004', '\005' : '\\005', - '\006' : '\\006', '\007' : '\\007', '\010' : '\\010', - '\011' : '\\011', '\012' : '\\012', '\013' : '\\013', - '\014' : '\\014', '\015' : '\\015', '\016' : '\\016', - '\017' : '\\017', '\020' : '\\020', '\021' : '\\021', - '\022' : '\\022', '\023' : '\\023', '\024' : '\\024', - '\025' : '\\025', '\026' : '\\026', '\027' : '\\027', - '\030' : '\\030', '\031' : '\\031', '\032' : '\\032', - '\033' : '\\033', '\034' : '\\034', '\035' : '\\035', - '\036' : '\\036', '\037' : '\\037', - - # Because of the way browsers really handle cookies (as opposed - # to what the RFC says) we also encode , and ; - - ',' : '\\054', ';' : '\\073', - - '"' : '\\"', '\\' : '\\\\', - - '\177' : '\\177', '\200' : '\\200', '\201' : '\\201', - '\202' : '\\202', '\203' : '\\203', '\204' : '\\204', - '\205' : '\\205', '\206' : '\\206', '\207' : '\\207', - '\210' : '\\210', '\211' : '\\211', '\212' : '\\212', - '\213' : '\\213', '\214' : '\\214', '\215' : '\\215', - '\216' : '\\216', '\217' : '\\217', '\220' : '\\220', - '\221' : '\\221', '\222' : '\\222', '\223' : '\\223', - '\224' : '\\224', '\225' : '\\225', '\226' : '\\226', - '\227' : '\\227', '\230' : '\\230', '\231' : '\\231', - '\232' : '\\232', '\233' : '\\233', '\234' : '\\234', - '\235' : '\\235', '\236' : '\\236', '\237' : '\\237', - '\240' : '\\240', '\241' : '\\241', '\242' : '\\242', - '\243' : '\\243', '\244' : '\\244', '\245' : '\\245', - '\246' : '\\246', '\247' : '\\247', '\250' : '\\250', - '\251' : '\\251', '\252' : '\\252', '\253' : '\\253', - '\254' : '\\254', '\255' : '\\255', '\256' : '\\256', - '\257' : '\\257', '\260' : '\\260', '\261' : '\\261', - '\262' : '\\262', '\263' : '\\263', '\264' : '\\264', - '\265' : '\\265', '\266' : '\\266', '\267' : '\\267', - '\270' : '\\270', '\271' : '\\271', '\272' : '\\272', - '\273' : '\\273', '\274' : '\\274', '\275' : '\\275', - '\276' : '\\276', '\277' : '\\277', '\300' : '\\300', - '\301' : '\\301', '\302' : '\\302', '\303' : '\\303', - '\304' : '\\304', '\305' : '\\305', '\306' : '\\306', - '\307' : '\\307', '\310' : '\\310', '\311' : '\\311', - '\312' : '\\312', '\313' : '\\313', '\314' : '\\314', - '\315' : '\\315', '\316' : '\\316', '\317' : '\\317', - '\320' : '\\320', '\321' : '\\321', '\322' : '\\322', - '\323' : '\\323', '\324' : '\\324', '\325' : '\\325', - '\326' : '\\326', '\327' : '\\327', '\330' : '\\330', - '\331' : '\\331', '\332' : '\\332', '\333' : '\\333', - '\334' : '\\334', '\335' : '\\335', '\336' : '\\336', - '\337' : '\\337', '\340' : '\\340', '\341' : '\\341', - '\342' : '\\342', '\343' : '\\343', '\344' : '\\344', - '\345' : '\\345', '\346' : '\\346', '\347' : '\\347', - '\350' : '\\350', '\351' : '\\351', '\352' : '\\352', - '\353' : '\\353', '\354' : '\\354', '\355' : '\\355', - '\356' : '\\356', '\357' : '\\357', '\360' : '\\360', - '\361' : '\\361', '\362' : '\\362', '\363' : '\\363', - '\364' : '\\364', '\365' : '\\365', '\366' : '\\366', - '\367' : '\\367', '\370' : '\\370', '\371' : '\\371', - '\372' : '\\372', '\373' : '\\373', '\374' : '\\374', - '\375' : '\\375', '\376' : '\\376', '\377' : '\\377' - } - -def _quote(str, LegalChars=_LegalChars): - r"""Quote a string for use in a cookie header. - - If the string does not need to be double-quoted, then just return the - string. Otherwise, surround the string in doublequotes and quote - (with a \) special characters. - """ - if all(c in LegalChars for c in str): - return str - else: - return '"' + _nulljoin(_Translator.get(s, s) for s in str) + '"' - - -_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") -_QuotePatt = re.compile(r"[\\].") - -def _unquote(mystr): - # If there aren't any doublequotes, - # then there can't be any special characters. See RFC 2109. - if len(mystr) < 2: - return mystr - if mystr[0] != '"' or mystr[-1] != '"': - return mystr - - # We have to assume that we must decode this string. - # Down to work. - - # Remove the "s - mystr = mystr[1:-1] - - # Check for special sequences. Examples: - # \012 --> \n - # \" --> " - # - i = 0 - n = len(mystr) - res = [] - while 0 <= i < n: - o_match = _OctalPatt.search(mystr, i) - q_match = _QuotePatt.search(mystr, i) - if not o_match and not q_match: # Neither matched - res.append(mystr[i:]) - break - # else: - j = k = -1 - if o_match: - j = o_match.start(0) - if q_match: - k = q_match.start(0) - if q_match and (not o_match or k < j): # QuotePatt matched - res.append(mystr[i:k]) - res.append(mystr[k+1]) - i = k + 2 - else: # OctalPatt matched - res.append(mystr[i:j]) - res.append(chr(int(mystr[j+1:j+4], 8))) - i = j + 4 - return _nulljoin(res) - -# The _getdate() routine is used to set the expiration time in the cookie's HTTP -# header. By default, _getdate() returns the current time in the appropriate -# "expires" format for a Set-Cookie header. The one optional argument is an -# offset from now, in seconds. For example, an offset of -3600 means "one hour -# ago". The offset may be a floating point number. -# - -_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - -_monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - -def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname): - from time import gmtime, time - now = time() - year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future) - return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \ - (weekdayname[wd], day, monthname[month], year, hh, mm, ss) - - -class Morsel(dict): - """A class to hold ONE (key, value) pair. - - In a cookie, each such pair may have several attributes, so this class is - used to keep the attributes associated with the appropriate key,value pair. - This class also includes a coded_value attribute, which is used to hold - the network representation of the value. This is most useful when Python - objects are pickled for network transit. - """ - # RFC 2109 lists these attributes as reserved: - # path comment domain - # max-age secure version - # - # For historical reasons, these attributes are also reserved: - # expires - # - # This is an extension from Microsoft: - # httponly - # - # This dictionary provides a mapping from the lowercase - # variant on the left to the appropriate traditional - # formatting on the right. - _reserved = { - "expires" : "expires", - "path" : "Path", - "comment" : "Comment", - "domain" : "Domain", - "max-age" : "Max-Age", - "secure" : "secure", - "httponly" : "httponly", - "version" : "Version", - } - - _flags = set(['secure', 'httponly']) - - def __init__(self): - # Set defaults - self.key = self.value = self.coded_value = None - - # Set default attributes - for key in self._reserved: - dict.__setitem__(self, key, "") - - def __setitem__(self, K, V): - K = K.lower() - if not K in self._reserved: - raise CookieError("Invalid Attribute %s" % K) - dict.__setitem__(self, K, V) - - def isReservedKey(self, K): - return K.lower() in self._reserved - - def set(self, key, val, coded_val, LegalChars=_LegalChars): - # First we verify that the key isn't a reserved word - # Second we make sure it only contains legal characters - if key.lower() in self._reserved: - raise CookieError("Attempt to set a reserved key: %s" % key) - if any(c not in LegalChars for c in key): - raise CookieError("Illegal key value: %s" % key) - - # It's a good key, so save it. - self.key = key - self.value = val - self.coded_value = coded_val - - def output(self, attrs=None, header="Set-Cookie:"): - return "%s %s" % (header, self.OutputString(attrs)) - - __str__ = output - - @as_native_str() - def __repr__(self): - if PY2 and isinstance(self.value, unicode): - val = str(self.value) # make it a newstr to remove the u prefix - else: - val = self.value - return '<%s: %s=%s>' % (self.__class__.__name__, - str(self.key), repr(val)) - - def js_output(self, attrs=None): - # Print javascript - return """ - - """ % (self.OutputString(attrs).replace('"', r'\"')) - - def OutputString(self, attrs=None): - # Build up our result - # - result = [] - append = result.append - - # First, the key=value pair - append("%s=%s" % (self.key, self.coded_value)) - - # Now add any defined attributes - if attrs is None: - attrs = self._reserved - items = sorted(self.items()) - for key, value in items: - if value == "": - continue - if key not in attrs: - continue - if key == "expires" and isinstance(value, int): - append("%s=%s" % (self._reserved[key], _getdate(value))) - elif key == "max-age" and isinstance(value, int): - append("%s=%d" % (self._reserved[key], value)) - elif key == "secure": - append(str(self._reserved[key])) - elif key == "httponly": - append(str(self._reserved[key])) - else: - append("%s=%s" % (self._reserved[key], value)) - - # Return the result - return _semispacejoin(result) - - -# -# Pattern for finding cookie -# -# This used to be strict parsing based on the RFC2109 and RFC2068 -# specifications. I have since discovered that MSIE 3.0x doesn't -# follow the character rules outlined in those specs. As a -# result, the parsing rules here are less strict. -# - -_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" -_CookiePattern = re.compile(r""" - (?x) # This is a verbose pattern - (?P # Start of group 'key' - """ + _LegalCharsPatt + r"""+? # Any word of at least one letter - ) # End of group 'key' - ( # Optional group: there may not be a value. - \s*=\s* # Equal Sign - (?P # Start of group 'val' - "(?:[^\\"]|\\.)*" # Any doublequoted string - | # or - \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr - | # or - """ + _LegalCharsPatt + r"""* # Any word or empty string - ) # End of group 'val' - )? # End of optional value group - \s* # Any number of spaces. - (\s+|;|$) # Ending either at space, semicolon, or EOS. - """, re.ASCII) # May be removed if safe. - - -# At long last, here is the cookie class. Using this class is almost just like -# using a dictionary. See this module's docstring for example usage. -# -class BaseCookie(dict): - """A container class for a set of Morsels.""" - - def value_decode(self, val): - """real_value, coded_value = value_decode(STRING) - Called prior to setting a cookie's value from the network - representation. The VALUE is the value read from HTTP - header. - Override this function to modify the behavior of cookies. - """ - return val, val - - def value_encode(self, val): - """real_value, coded_value = value_encode(VALUE) - Called prior to setting a cookie's value from the dictionary - representation. The VALUE is the value being assigned. - Override this function to modify the behavior of cookies. - """ - strval = str(val) - return strval, strval - - def __init__(self, input=None): - if input: - self.load(input) - - def __set(self, key, real_value, coded_value): - """Private method for setting a cookie's value""" - M = self.get(key, Morsel()) - M.set(key, real_value, coded_value) - dict.__setitem__(self, key, M) - - def __setitem__(self, key, value): - """Dictionary style assignment.""" - rval, cval = self.value_encode(value) - self.__set(key, rval, cval) - - def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"): - """Return a string suitable for HTTP.""" - result = [] - items = sorted(self.items()) - for key, value in items: - result.append(value.output(attrs, header)) - return sep.join(result) - - __str__ = output - - @as_native_str() - def __repr__(self): - l = [] - items = sorted(self.items()) - for key, value in items: - if PY2 and isinstance(value.value, unicode): - val = str(value.value) # make it a newstr to remove the u prefix - else: - val = value.value - l.append('%s=%s' % (str(key), repr(val))) - return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l)) - - def js_output(self, attrs=None): - """Return a string suitable for JavaScript.""" - result = [] - items = sorted(self.items()) - for key, value in items: - result.append(value.js_output(attrs)) - return _nulljoin(result) - - def load(self, rawdata): - """Load cookies from a string (presumably HTTP_COOKIE) or - from a dictionary. Loading cookies from a dictionary 'd' - is equivalent to calling: - map(Cookie.__setitem__, d.keys(), d.values()) - """ - if isinstance(rawdata, str): - self.__parse_string(rawdata) - else: - # self.update() wouldn't call our custom __setitem__ - for key, value in rawdata.items(): - self[key] = value - return - - def __parse_string(self, mystr, patt=_CookiePattern): - i = 0 # Our starting point - n = len(mystr) # Length of string - M = None # current morsel - - while 0 <= i < n: - # Start looking for a cookie - match = patt.search(mystr, i) - if not match: - # No more cookies - break - - key, value = match.group("key"), match.group("val") - - i = match.end(0) - - # Parse the key, value in case it's metainfo - if key[0] == "$": - # We ignore attributes which pertain to the cookie - # mechanism as a whole. See RFC 2109. - # (Does anyone care?) - if M: - M[key[1:]] = value - elif key.lower() in Morsel._reserved: - if M: - if value is None: - if key.lower() in Morsel._flags: - M[key] = True - else: - M[key] = _unquote(value) - elif value is not None: - rval, cval = self.value_decode(value) - self.__set(key, rval, cval) - M = self[key] - - -class SimpleCookie(BaseCookie): - """ - SimpleCookie supports strings as cookie values. When setting - the value using the dictionary assignment notation, SimpleCookie - calls the builtin str() to convert the value to a string. Values - received from HTTP are kept as strings. - """ - def value_decode(self, val): - return _unquote(val), val - - def value_encode(self, val): - strval = str(val) - return strval, _quote(strval) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/server.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/server.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/http/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/http/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1226 +0,0 @@ -"""HTTP server classes. - -From Python 3.3 - -Note: BaseHTTPRequestHandler doesn't implement any HTTP request; see -SimpleHTTPRequestHandler for simple implementations of GET, HEAD and POST, -and CGIHTTPRequestHandler for CGI scripts. - -It does, however, optionally implement HTTP/1.1 persistent connections, -as of version 0.3. - -Notes on CGIHTTPRequestHandler ------------------------------- - -This class implements GET and POST requests to cgi-bin scripts. - -If the os.fork() function is not present (e.g. on Windows), -subprocess.Popen() is used as a fallback, with slightly altered semantics. - -In all cases, the implementation is intentionally naive -- all -requests are executed synchronously. - -SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL --- it may execute arbitrary Python code or external programs. - -Note that status code 200 is sent prior to execution of a CGI script, so -scripts cannot send other status codes such as 302 (redirect). - -XXX To do: - -- log requests even later (to capture byte count) -- log user-agent header and other interesting goodies -- send error log to separate file -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future import utils -from future.builtins import * - - -# See also: -# -# HTTP Working Group T. Berners-Lee -# INTERNET-DRAFT R. T. Fielding -# H. Frystyk Nielsen -# Expires September 8, 1995 March 8, 1995 -# -# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt -# -# and -# -# Network Working Group R. Fielding -# Request for Comments: 2616 et al -# Obsoletes: 2068 June 1999 -# Category: Standards Track -# -# URL: http://www.faqs.org/rfcs/rfc2616.html - -# Log files -# --------- -# -# Here's a quote from the NCSA httpd docs about log file format. -# -# | The logfile format is as follows. Each line consists of: -# | -# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb -# | -# | host: Either the DNS name or the IP number of the remote client -# | rfc931: Any information returned by identd for this person, -# | - otherwise. -# | authuser: If user sent a userid for authentication, the user name, -# | - otherwise. -# | DD: Day -# | Mon: Month (calendar name) -# | YYYY: Year -# | hh: hour (24-hour format, the machine's timezone) -# | mm: minutes -# | ss: seconds -# | request: The first line of the HTTP request as sent by the client. -# | ddd: the status code returned by the server, - if not available. -# | bbbb: the total number of bytes sent, -# | *not including the HTTP/1.0 header*, - if not available -# | -# | You can determine the name of the file accessed through request. -# -# (Actually, the latter is only true if you know the server configuration -# at the time the request was made!) - -__version__ = "0.6" - -__all__ = ["HTTPServer", "BaseHTTPRequestHandler"] - -from future.backports import html -from future.backports.http import client as http_client -from future.backports.urllib import parse as urllib_parse -from future.backports import socketserver - -import io -import mimetypes -import os -import posixpath -import select -import shutil -import socket # For gethostbyaddr() -import sys -import time -import copy -import argparse - - -# Default error message template -DEFAULT_ERROR_MESSAGE = """\ - - - - - Error response - - -

Error response

-

Error code: %(code)d

-

Message: %(message)s.

-

Error code explanation: %(code)s - %(explain)s.

- - -""" - -DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8" - -def _quote_html(html): - return html.replace("&", "&").replace("<", "<").replace(">", ">") - -class HTTPServer(socketserver.TCPServer): - - allow_reuse_address = 1 # Seems to make sense in testing environment - - def server_bind(self): - """Override server_bind to store the server name.""" - socketserver.TCPServer.server_bind(self) - host, port = self.socket.getsockname()[:2] - self.server_name = socket.getfqdn(host) - self.server_port = port - - -class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): - - """HTTP request handler base class. - - The following explanation of HTTP serves to guide you through the - code as well as to expose any misunderstandings I may have about - HTTP (so you don't need to read the code to figure out I'm wrong - :-). - - HTTP (HyperText Transfer Protocol) is an extensible protocol on - top of a reliable stream transport (e.g. TCP/IP). The protocol - recognizes three parts to a request: - - 1. One line identifying the request type and path - 2. An optional set of RFC-822-style headers - 3. An optional data part - - The headers and data are separated by a blank line. - - The first line of the request has the form - - - - where is a (case-sensitive) keyword such as GET or POST, - is a string containing path information for the request, - and should be the string "HTTP/1.0" or "HTTP/1.1". - is encoded using the URL encoding scheme (using %xx to signify - the ASCII character with hex code xx). - - The specification specifies that lines are separated by CRLF but - for compatibility with the widest range of clients recommends - servers also handle LF. Similarly, whitespace in the request line - is treated sensibly (allowing multiple spaces between components - and allowing trailing whitespace). - - Similarly, for output, lines ought to be separated by CRLF pairs - but most clients grok LF characters just fine. - - If the first line of the request has the form - - - - (i.e. is left out) then this is assumed to be an HTTP - 0.9 request; this form has no optional headers and data part and - the reply consists of just the data. - - The reply form of the HTTP 1.x protocol again has three parts: - - 1. One line giving the response code - 2. An optional set of RFC-822-style headers - 3. The data - - Again, the headers and data are separated by a blank line. - - The response code line has the form - - - - where is the protocol version ("HTTP/1.0" or "HTTP/1.1"), - is a 3-digit response code indicating success or - failure of the request, and is an optional - human-readable string explaining what the response code means. - - This server parses the request and the headers, and then calls a - function specific to the request type (). Specifically, - a request SPAM will be handled by a method do_SPAM(). If no - such method exists the server sends an error response to the - client. If it exists, it is called with no arguments: - - do_SPAM() - - Note that the request name is case sensitive (i.e. SPAM and spam - are different requests). - - The various request details are stored in instance variables: - - - client_address is the client IP address in the form (host, - port); - - - command, path and version are the broken-down request line; - - - headers is an instance of email.message.Message (or a derived - class) containing the header information; - - - rfile is a file object open for reading positioned at the - start of the optional input data part; - - - wfile is a file object open for writing. - - IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING! - - The first thing to be written must be the response line. Then - follow 0 or more header lines, then a blank line, and then the - actual data (if any). The meaning of the header lines depends on - the command executed by the server; in most cases, when data is - returned, there should be at least one header line of the form - - Content-type: / - - where and should be registered MIME types, - e.g. "text/html" or "text/plain". - - """ - - # The Python system version, truncated to its first component. - sys_version = "Python/" + sys.version.split()[0] - - # The server software version. You may want to override this. - # The format is multiple whitespace-separated strings, - # where each string is of the form name[/version]. - server_version = "BaseHTTP/" + __version__ - - error_message_format = DEFAULT_ERROR_MESSAGE - error_content_type = DEFAULT_ERROR_CONTENT_TYPE - - # The default request version. This only affects responses up until - # the point where the request line is parsed, so it mainly decides what - # the client gets back when sending a malformed request line. - # Most web servers default to HTTP 0.9, i.e. don't send a status line. - default_request_version = "HTTP/0.9" - - def parse_request(self): - """Parse a request (internal). - - The request should be stored in self.raw_requestline; the results - are in self.command, self.path, self.request_version and - self.headers. - - Return True for success, False for failure; on failure, an - error is sent back. - - """ - self.command = None # set in case of error on the first line - self.request_version = version = self.default_request_version - self.close_connection = 1 - requestline = str(self.raw_requestline, 'iso-8859-1') - requestline = requestline.rstrip('\r\n') - self.requestline = requestline - words = requestline.split() - if len(words) == 3: - command, path, version = words - if version[:5] != 'HTTP/': - self.send_error(400, "Bad request version (%r)" % version) - return False - try: - base_version_number = version.split('/', 1)[1] - version_number = base_version_number.split(".") - # RFC 2145 section 3.1 says there can be only one "." and - # - major and minor numbers MUST be treated as - # separate integers; - # - HTTP/2.4 is a lower version than HTTP/2.13, which in - # turn is lower than HTTP/12.3; - # - Leading zeros MUST be ignored by recipients. - if len(version_number) != 2: - raise ValueError - version_number = int(version_number[0]), int(version_number[1]) - except (ValueError, IndexError): - self.send_error(400, "Bad request version (%r)" % version) - return False - if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": - self.close_connection = 0 - if version_number >= (2, 0): - self.send_error(505, - "Invalid HTTP Version (%s)" % base_version_number) - return False - elif len(words) == 2: - command, path = words - self.close_connection = 1 - if command != 'GET': - self.send_error(400, - "Bad HTTP/0.9 request type (%r)" % command) - return False - elif not words: - return False - else: - self.send_error(400, "Bad request syntax (%r)" % requestline) - return False - self.command, self.path, self.request_version = command, path, version - - # Examine the headers and look for a Connection directive. - try: - self.headers = http_client.parse_headers(self.rfile, - _class=self.MessageClass) - except http_client.LineTooLong: - self.send_error(400, "Line too long") - return False - - conntype = self.headers.get('Connection', "") - if conntype.lower() == 'close': - self.close_connection = 1 - elif (conntype.lower() == 'keep-alive' and - self.protocol_version >= "HTTP/1.1"): - self.close_connection = 0 - # Examine the headers and look for an Expect directive - expect = self.headers.get('Expect', "") - if (expect.lower() == "100-continue" and - self.protocol_version >= "HTTP/1.1" and - self.request_version >= "HTTP/1.1"): - if not self.handle_expect_100(): - return False - return True - - def handle_expect_100(self): - """Decide what to do with an "Expect: 100-continue" header. - - If the client is expecting a 100 Continue response, we must - respond with either a 100 Continue or a final response before - waiting for the request body. The default is to always respond - with a 100 Continue. You can behave differently (for example, - reject unauthorized requests) by overriding this method. - - This method should either return True (possibly after sending - a 100 Continue response) or send an error response and return - False. - - """ - self.send_response_only(100) - self.flush_headers() - return True - - def handle_one_request(self): - """Handle a single HTTP request. - - You normally don't need to override this method; see the class - __doc__ string for information on how to handle specific HTTP - commands such as GET and POST. - - """ - try: - self.raw_requestline = self.rfile.readline(65537) - if len(self.raw_requestline) > 65536: - self.requestline = '' - self.request_version = '' - self.command = '' - self.send_error(414) - return - if not self.raw_requestline: - self.close_connection = 1 - return - if not self.parse_request(): - # An error code has been sent, just exit - return - mname = 'do_' + self.command - if not hasattr(self, mname): - self.send_error(501, "Unsupported method (%r)" % self.command) - return - method = getattr(self, mname) - method() - self.wfile.flush() #actually send the response if not already done. - except socket.timeout as e: - #a read or a write timed out. Discard this connection - self.log_error("Request timed out: %r", e) - self.close_connection = 1 - return - - def handle(self): - """Handle multiple requests if necessary.""" - self.close_connection = 1 - - self.handle_one_request() - while not self.close_connection: - self.handle_one_request() - - def send_error(self, code, message=None): - """Send and log an error reply. - - Arguments are the error code, and a detailed message. - The detailed message defaults to the short entry matching the - response code. - - This sends an error response (so it must be called before any - output has been generated), logs the error, and finally sends - a piece of HTML explaining the error to the user. - - """ - - try: - shortmsg, longmsg = self.responses[code] - except KeyError: - shortmsg, longmsg = '???', '???' - if message is None: - message = shortmsg - explain = longmsg - self.log_error("code %d, message %s", code, message) - # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201) - content = (self.error_message_format % - {'code': code, 'message': _quote_html(message), 'explain': explain}) - self.send_response(code, message) - self.send_header("Content-Type", self.error_content_type) - self.send_header('Connection', 'close') - self.end_headers() - if self.command != 'HEAD' and code >= 200 and code not in (204, 304): - self.wfile.write(content.encode('UTF-8', 'replace')) - - def send_response(self, code, message=None): - """Add the response header to the headers buffer and log the - response code. - - Also send two standard headers with the server software - version and the current date. - - """ - self.log_request(code) - self.send_response_only(code, message) - self.send_header('Server', self.version_string()) - self.send_header('Date', self.date_time_string()) - - def send_response_only(self, code, message=None): - """Send the response header only.""" - if message is None: - if code in self.responses: - message = self.responses[code][0] - else: - message = '' - if self.request_version != 'HTTP/0.9': - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append(("%s %d %s\r\n" % - (self.protocol_version, code, message)).encode( - 'latin-1', 'strict')) - - def send_header(self, keyword, value): - """Send a MIME header to the headers buffer.""" - if self.request_version != 'HTTP/0.9': - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append( - ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) - - if keyword.lower() == 'connection': - if value.lower() == 'close': - self.close_connection = 1 - elif value.lower() == 'keep-alive': - self.close_connection = 0 - - def end_headers(self): - """Send the blank line ending the MIME headers.""" - if self.request_version != 'HTTP/0.9': - self._headers_buffer.append(b"\r\n") - self.flush_headers() - - def flush_headers(self): - if hasattr(self, '_headers_buffer'): - self.wfile.write(b"".join(self._headers_buffer)) - self._headers_buffer = [] - - def log_request(self, code='-', size='-'): - """Log an accepted request. - - This is called by send_response(). - - """ - - self.log_message('"%s" %s %s', - self.requestline, str(code), str(size)) - - def log_error(self, format, *args): - """Log an error. - - This is called when a request cannot be fulfilled. By - default it passes the message on to log_message(). - - Arguments are the same as for log_message(). - - XXX This should go to the separate error log. - - """ - - self.log_message(format, *args) - - def log_message(self, format, *args): - """Log an arbitrary message. - - This is used by all other logging functions. Override - it if you have specific logging wishes. - - The first argument, FORMAT, is a format string for the - message to be logged. If the format string contains - any % escapes requiring parameters, they should be - specified as subsequent arguments (it's just like - printf!). - - The client ip and current date/time are prefixed to - every message. - - """ - - sys.stderr.write("%s - - [%s] %s\n" % - (self.address_string(), - self.log_date_time_string(), - format%args)) - - def version_string(self): - """Return the server software version string.""" - return self.server_version + ' ' + self.sys_version - - def date_time_string(self, timestamp=None): - """Return the current date and time formatted for a message header.""" - if timestamp is None: - timestamp = time.time() - year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) - s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( - self.weekdayname[wd], - day, self.monthname[month], year, - hh, mm, ss) - return s - - def log_date_time_string(self): - """Return the current time formatted for logging.""" - now = time.time() - year, month, day, hh, mm, ss, x, y, z = time.localtime(now) - s = "%02d/%3s/%04d %02d:%02d:%02d" % ( - day, self.monthname[month], year, hh, mm, ss) - return s - - weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - - monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - - def address_string(self): - """Return the client address.""" - - return self.client_address[0] - - # Essentially static class variables - - # The version of the HTTP protocol we support. - # Set this to HTTP/1.1 to enable automatic keepalive - protocol_version = "HTTP/1.0" - - # MessageClass used to parse headers - MessageClass = http_client.HTTPMessage - - # Table mapping response codes to messages; entries have the - # form {code: (shortmessage, longmessage)}. - # See RFC 2616 and 6585. - responses = { - 100: ('Continue', 'Request received, please continue'), - 101: ('Switching Protocols', - 'Switching to new protocol; obey Upgrade header'), - - 200: ('OK', 'Request fulfilled, document follows'), - 201: ('Created', 'Document created, URL follows'), - 202: ('Accepted', - 'Request accepted, processing continues off-line'), - 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), - 204: ('No Content', 'Request fulfilled, nothing follows'), - 205: ('Reset Content', 'Clear input form for further input.'), - 206: ('Partial Content', 'Partial content follows.'), - - 300: ('Multiple Choices', - 'Object has several resources -- see URI list'), - 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), - 302: ('Found', 'Object moved temporarily -- see URI list'), - 303: ('See Other', 'Object moved -- see Method and URL list'), - 304: ('Not Modified', - 'Document has not changed since given time'), - 305: ('Use Proxy', - 'You must use proxy specified in Location to access this ' - 'resource.'), - 307: ('Temporary Redirect', - 'Object moved temporarily -- see URI list'), - - 400: ('Bad Request', - 'Bad request syntax or unsupported method'), - 401: ('Unauthorized', - 'No permission -- see authorization schemes'), - 402: ('Payment Required', - 'No payment -- see charging schemes'), - 403: ('Forbidden', - 'Request forbidden -- authorization will not help'), - 404: ('Not Found', 'Nothing matches the given URI'), - 405: ('Method Not Allowed', - 'Specified method is invalid for this resource.'), - 406: ('Not Acceptable', 'URI not available in preferred format.'), - 407: ('Proxy Authentication Required', 'You must authenticate with ' - 'this proxy before proceeding.'), - 408: ('Request Timeout', 'Request timed out; try again later.'), - 409: ('Conflict', 'Request conflict.'), - 410: ('Gone', - 'URI no longer exists and has been permanently removed.'), - 411: ('Length Required', 'Client must specify Content-Length.'), - 412: ('Precondition Failed', 'Precondition in headers is false.'), - 413: ('Request Entity Too Large', 'Entity is too large.'), - 414: ('Request-URI Too Long', 'URI is too long.'), - 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), - 416: ('Requested Range Not Satisfiable', - 'Cannot satisfy request range.'), - 417: ('Expectation Failed', - 'Expect condition could not be satisfied.'), - 428: ('Precondition Required', - 'The origin server requires the request to be conditional.'), - 429: ('Too Many Requests', 'The user has sent too many requests ' - 'in a given amount of time ("rate limiting").'), - 431: ('Request Header Fields Too Large', 'The server is unwilling to ' - 'process the request because its header fields are too large.'), - - 500: ('Internal Server Error', 'Server got itself in trouble'), - 501: ('Not Implemented', - 'Server does not support this operation'), - 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), - 503: ('Service Unavailable', - 'The server cannot process the request due to a high load'), - 504: ('Gateway Timeout', - 'The gateway server did not receive a timely response'), - 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), - 511: ('Network Authentication Required', - 'The client needs to authenticate to gain network access.'), - } - - -class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): - - """Simple HTTP request handler with GET and HEAD commands. - - This serves files from the current directory and any of its - subdirectories. The MIME type for files is determined by - calling the .guess_type() method. - - The GET and HEAD requests are identical except that the HEAD - request omits the actual contents of the file. - - """ - - server_version = "SimpleHTTP/" + __version__ - - def do_GET(self): - """Serve a GET request.""" - f = self.send_head() - if f: - self.copyfile(f, self.wfile) - f.close() - - def do_HEAD(self): - """Serve a HEAD request.""" - f = self.send_head() - if f: - f.close() - - def send_head(self): - """Common code for GET and HEAD commands. - - This sends the response code and MIME headers. - - Return value is either a file object (which has to be copied - to the outputfile by the caller unless the command was HEAD, - and must be closed by the caller under all circumstances), or - None, in which case the caller has nothing further to do. - - """ - path = self.translate_path(self.path) - f = None - if os.path.isdir(path): - if not self.path.endswith('/'): - # redirect browser - doing basically what apache does - self.send_response(301) - self.send_header("Location", self.path + "/") - self.end_headers() - return None - for index in "index.html", "index.htm": - index = os.path.join(path, index) - if os.path.exists(index): - path = index - break - else: - return self.list_directory(path) - ctype = self.guess_type(path) - try: - f = open(path, 'rb') - except IOError: - self.send_error(404, "File not found") - return None - self.send_response(200) - self.send_header("Content-type", ctype) - fs = os.fstat(f.fileno()) - self.send_header("Content-Length", str(fs[6])) - self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) - self.end_headers() - return f - - def list_directory(self, path): - """Helper to produce a directory listing (absent index.html). - - Return value is either a file object, or None (indicating an - error). In either case, the headers are sent, making the - interface the same as for send_head(). - - """ - try: - list = os.listdir(path) - except os.error: - self.send_error(404, "No permission to list directory") - return None - list.sort(key=lambda a: a.lower()) - r = [] - displaypath = html.escape(urllib_parse.unquote(self.path)) - enc = sys.getfilesystemencoding() - title = 'Directory listing for %s' % displaypath - r.append('') - r.append('\n') - r.append('' % enc) - r.append('%s\n' % title) - r.append('\n

%s

' % title) - r.append('
\n
    ') - for name in list: - fullname = os.path.join(path, name) - displayname = linkname = name - # Append / for directories or @ for symbolic links - if os.path.isdir(fullname): - displayname = name + "/" - linkname = name + "/" - if os.path.islink(fullname): - displayname = name + "@" - # Note: a link to a directory displays with @ and links with / - r.append('
  • %s
  • ' - % (urllib_parse.quote(linkname), html.escape(displayname))) - # # Use this instead: - # r.append('
  • %s
  • ' - # % (urllib.quote(linkname), cgi.escape(displayname))) - r.append('
\n
\n\n\n') - encoded = '\n'.join(r).encode(enc) - f = io.BytesIO() - f.write(encoded) - f.seek(0) - self.send_response(200) - self.send_header("Content-type", "text/html; charset=%s" % enc) - self.send_header("Content-Length", str(len(encoded))) - self.end_headers() - return f - - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - # abandon query parameters - path = path.split('?',1)[0] - path = path.split('#',1)[0] - path = posixpath.normpath(urllib_parse.unquote(path)) - words = path.split('/') - words = filter(None, words) - path = os.getcwd() - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - if word in (os.curdir, os.pardir): continue - path = os.path.join(path, word) - return path - - def copyfile(self, source, outputfile): - """Copy all data between two file objects. - - The SOURCE argument is a file object open for reading - (or anything with a read() method) and the DESTINATION - argument is a file object open for writing (or - anything with a write() method). - - The only reason for overriding this would be to change - the block size or perhaps to replace newlines by CRLF - -- note however that this the default server uses this - to copy binary data as well. - - """ - shutil.copyfileobj(source, outputfile) - - def guess_type(self, path): - """Guess the type of a file. - - Argument is a PATH (a filename). - - Return value is a string of the form type/subtype, - usable for a MIME Content-type header. - - The default implementation looks the file's extension - up in the table self.extensions_map, using application/octet-stream - as a default; however it would be permissible (if - slow) to look inside the data to make a better guess. - - """ - - base, ext = posixpath.splitext(path) - if ext in self.extensions_map: - return self.extensions_map[ext] - ext = ext.lower() - if ext in self.extensions_map: - return self.extensions_map[ext] - else: - return self.extensions_map[''] - - if not mimetypes.inited: - mimetypes.init() # try to read system mime.types - extensions_map = mimetypes.types_map.copy() - extensions_map.update({ - '': 'application/octet-stream', # Default - '.py': 'text/plain', - '.c': 'text/plain', - '.h': 'text/plain', - }) - - -# Utilities for CGIHTTPRequestHandler - -def _url_collapse_path(path): - """ - Given a URL path, remove extra '/'s and '.' path elements and collapse - any '..' references and returns a colllapsed path. - - Implements something akin to RFC-2396 5.2 step 6 to parse relative paths. - The utility of this function is limited to is_cgi method and helps - preventing some security attacks. - - Returns: A tuple of (head, tail) where tail is everything after the final / - and head is everything before it. Head will always start with a '/' and, - if it contains anything else, never have a trailing '/'. - - Raises: IndexError if too many '..' occur within the path. - - """ - # Similar to os.path.split(os.path.normpath(path)) but specific to URL - # path semantics rather than local operating system semantics. - path_parts = path.split('/') - head_parts = [] - for part in path_parts[:-1]: - if part == '..': - head_parts.pop() # IndexError if more '..' than prior parts - elif part and part != '.': - head_parts.append( part ) - if path_parts: - tail_part = path_parts.pop() - if tail_part: - if tail_part == '..': - head_parts.pop() - tail_part = '' - elif tail_part == '.': - tail_part = '' - else: - tail_part = '' - - splitpath = ('/' + '/'.join(head_parts), tail_part) - collapsed_path = "/".join(splitpath) - - return collapsed_path - - - -nobody = None - -def nobody_uid(): - """Internal routine to get nobody's uid""" - global nobody - if nobody: - return nobody - try: - import pwd - except ImportError: - return -1 - try: - nobody = pwd.getpwnam('nobody')[2] - except KeyError: - nobody = 1 + max(x[2] for x in pwd.getpwall()) - return nobody - - -def executable(path): - """Test for executable file.""" - return os.access(path, os.X_OK) - - -class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): - - """Complete HTTP server with GET, HEAD and POST commands. - - GET and HEAD also support running CGI scripts. - - The POST command is *only* implemented for CGI scripts. - - """ - - # Determine platform specifics - have_fork = hasattr(os, 'fork') - - # Make rfile unbuffered -- we need to read one line and then pass - # the rest to a subprocess, so we can't use buffered input. - rbufsize = 0 - - def do_POST(self): - """Serve a POST request. - - This is only implemented for CGI scripts. - - """ - - if self.is_cgi(): - self.run_cgi() - else: - self.send_error(501, "Can only POST to CGI scripts") - - def send_head(self): - """Version of send_head that support CGI scripts""" - if self.is_cgi(): - return self.run_cgi() - else: - return SimpleHTTPRequestHandler.send_head(self) - - def is_cgi(self): - """Test whether self.path corresponds to a CGI script. - - Returns True and updates the cgi_info attribute to the tuple - (dir, rest) if self.path requires running a CGI script. - Returns False otherwise. - - If any exception is raised, the caller should assume that - self.path was rejected as invalid and act accordingly. - - The default implementation tests whether the normalized url - path begins with one of the strings in self.cgi_directories - (and the next character is a '/' or the end of the string). - - """ - collapsed_path = _url_collapse_path(self.path) - dir_sep = collapsed_path.find('/', 1) - head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] - if head in self.cgi_directories: - self.cgi_info = head, tail - return True - return False - - - cgi_directories = ['/cgi-bin', '/htbin'] - - def is_executable(self, path): - """Test whether argument path is an executable file.""" - return executable(path) - - def is_python(self, path): - """Test whether argument path is a Python script.""" - head, tail = os.path.splitext(path) - return tail.lower() in (".py", ".pyw") - - def run_cgi(self): - """Execute a CGI script.""" - path = self.path - dir, rest = self.cgi_info - - i = path.find('/', len(dir) + 1) - while i >= 0: - nextdir = path[:i] - nextrest = path[i+1:] - - scriptdir = self.translate_path(nextdir) - if os.path.isdir(scriptdir): - dir, rest = nextdir, nextrest - i = path.find('/', len(dir) + 1) - else: - break - - # find an explicit query string, if present. - i = rest.rfind('?') - if i >= 0: - rest, query = rest[:i], rest[i+1:] - else: - query = '' - - # dissect the part after the directory name into a script name & - # a possible additional path, to be stored in PATH_INFO. - i = rest.find('/') - if i >= 0: - script, rest = rest[:i], rest[i:] - else: - script, rest = rest, '' - - scriptname = dir + '/' + script - scriptfile = self.translate_path(scriptname) - if not os.path.exists(scriptfile): - self.send_error(404, "No such CGI script (%r)" % scriptname) - return - if not os.path.isfile(scriptfile): - self.send_error(403, "CGI script is not a plain file (%r)" % - scriptname) - return - ispy = self.is_python(scriptname) - if self.have_fork or not ispy: - if not self.is_executable(scriptfile): - self.send_error(403, "CGI script is not executable (%r)" % - scriptname) - return - - # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html - # XXX Much of the following could be prepared ahead of time! - env = copy.deepcopy(os.environ) - env['SERVER_SOFTWARE'] = self.version_string() - env['SERVER_NAME'] = self.server.server_name - env['GATEWAY_INTERFACE'] = 'CGI/1.1' - env['SERVER_PROTOCOL'] = self.protocol_version - env['SERVER_PORT'] = str(self.server.server_port) - env['REQUEST_METHOD'] = self.command - uqrest = urllib_parse.unquote(rest) - env['PATH_INFO'] = uqrest - env['PATH_TRANSLATED'] = self.translate_path(uqrest) - env['SCRIPT_NAME'] = scriptname - if query: - env['QUERY_STRING'] = query - env['REMOTE_ADDR'] = self.client_address[0] - authorization = self.headers.get("authorization") - if authorization: - authorization = authorization.split() - if len(authorization) == 2: - import base64, binascii - env['AUTH_TYPE'] = authorization[0] - if authorization[0].lower() == "basic": - try: - authorization = authorization[1].encode('ascii') - if utils.PY3: - # In Py3.3, was: - authorization = base64.decodebytes(authorization).\ - decode('ascii') - else: - # Backport to Py2.7: - authorization = base64.decodestring(authorization).\ - decode('ascii') - except (binascii.Error, UnicodeError): - pass - else: - authorization = authorization.split(':') - if len(authorization) == 2: - env['REMOTE_USER'] = authorization[0] - # XXX REMOTE_IDENT - if self.headers.get('content-type') is None: - env['CONTENT_TYPE'] = self.headers.get_content_type() - else: - env['CONTENT_TYPE'] = self.headers['content-type'] - length = self.headers.get('content-length') - if length: - env['CONTENT_LENGTH'] = length - referer = self.headers.get('referer') - if referer: - env['HTTP_REFERER'] = referer - accept = [] - for line in self.headers.getallmatchingheaders('accept'): - if line[:1] in "\t\n\r ": - accept.append(line.strip()) - else: - accept = accept + line[7:].split(',') - env['HTTP_ACCEPT'] = ','.join(accept) - ua = self.headers.get('user-agent') - if ua: - env['HTTP_USER_AGENT'] = ua - co = filter(None, self.headers.get_all('cookie', [])) - cookie_str = ', '.join(co) - if cookie_str: - env['HTTP_COOKIE'] = cookie_str - # XXX Other HTTP_* headers - # Since we're setting the env in the parent, provide empty - # values to override previously set values - for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', - 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): - env.setdefault(k, "") - - self.send_response(200, "Script output follows") - self.flush_headers() - - decoded_query = query.replace('+', ' ') - - if self.have_fork: - # Unix -- fork as we should - args = [script] - if '=' not in decoded_query: - args.append(decoded_query) - nobody = nobody_uid() - self.wfile.flush() # Always flush before forking - pid = os.fork() - if pid != 0: - # Parent - pid, sts = os.waitpid(pid, 0) - # throw away additional data [see bug #427345] - while select.select([self.rfile], [], [], 0)[0]: - if not self.rfile.read(1): - break - if sts: - self.log_error("CGI script exit status %#x", sts) - return - # Child - try: - try: - os.setuid(nobody) - except os.error: - pass - os.dup2(self.rfile.fileno(), 0) - os.dup2(self.wfile.fileno(), 1) - os.execve(scriptfile, args, env) - except: - self.server.handle_error(self.request, self.client_address) - os._exit(127) - - else: - # Non-Unix -- use subprocess - import subprocess - cmdline = [scriptfile] - if self.is_python(scriptfile): - interp = sys.executable - if interp.lower().endswith("w.exe"): - # On Windows, use python.exe, not pythonw.exe - interp = interp[:-5] + interp[-4:] - cmdline = [interp, '-u'] + cmdline - if '=' not in query: - cmdline.append(query) - self.log_message("command: %s", subprocess.list2cmdline(cmdline)) - try: - nbytes = int(length) - except (TypeError, ValueError): - nbytes = 0 - p = subprocess.Popen(cmdline, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env = env - ) - if self.command.lower() == "post" and nbytes > 0: - data = self.rfile.read(nbytes) - else: - data = None - # throw away additional data [see bug #427345] - while select.select([self.rfile._sock], [], [], 0)[0]: - if not self.rfile._sock.recv(1): - break - stdout, stderr = p.communicate(data) - self.wfile.write(stdout) - if stderr: - self.log_error('%s', stderr) - p.stderr.close() - p.stdout.close() - status = p.returncode - if status: - self.log_error("CGI script exit status %#x", status) - else: - self.log_message("CGI script exited OK") - - -def test(HandlerClass = BaseHTTPRequestHandler, - ServerClass = HTTPServer, protocol="HTTP/1.0", port=8000): - """Test the HTTP request handler class. - - This runs an HTTP server on port 8000 (or the first command line - argument). - - """ - server_address = ('', port) - - HandlerClass.protocol_version = protocol - httpd = ServerClass(server_address, HandlerClass) - - sa = httpd.socket.getsockname() - print("Serving HTTP on", sa[0], "port", sa[1], "...") - try: - httpd.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - httpd.server_close() - sys.exit(0) - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--cgi', action='store_true', - help='Run as CGI Server') - parser.add_argument('port', action='store', - default=8000, type=int, - nargs='?', - help='Specify alternate port [default: 8000]') - args = parser.parse_args() - if args.cgi: - test(HandlerClass=CGIHTTPRequestHandler, port=args.port) - else: - test(HandlerClass=SimpleHTTPRequestHandler, port=args.port) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -""" -future.backports package -""" - -from __future__ import absolute_import - -import sys - -__future_module__ = True -from future.standard_library import import_top_level_modules - - -if sys.version_info[0] == 3: - import_top_level_modules() - - -from .misc import (ceil, - OrderedDict, - Counter, - ChainMap, - check_output, - count, - recursive_repr, - _count_elements, - cmp_to_key - ) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/_markupbase.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/_markupbase.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/_markupbase.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/_markupbase.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,422 +0,0 @@ -"""Shared support for scanning document type declarations in HTML and XHTML. - -Backported for python-future from Python 3.3. Reason: ParserBase is an -old-style class in the Python 2.7 source of markupbase.py, which I suspect -might be the cause of sporadic unit-test failures on travis-ci.org with -test_htmlparser.py. The test failures look like this: - - ====================================================================== - -ERROR: test_attr_entity_replacement (future.tests.test_htmlparser.AttributesStrictTestCase) - ----------------------------------------------------------------------- - -Traceback (most recent call last): - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 661, in test_attr_entity_replacement - [("starttag", "a", [("b", "&><\"'")])]) - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 93, in _run_check - collector = self.get_collector() - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 617, in get_collector - return EventCollector(strict=True) - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 27, in __init__ - html.parser.HTMLParser.__init__(self, *args, **kw) - File "/home/travis/build/edschofield/python-future/future/backports/html/parser.py", line 135, in __init__ - self.reset() - File "/home/travis/build/edschofield/python-future/future/backports/html/parser.py", line 143, in reset - _markupbase.ParserBase.reset(self) - -TypeError: unbound method reset() must be called with ParserBase instance as first argument (got EventCollector instance instead) - -This module is used as a foundation for the html.parser module. It has no -documented public API and should not be used directly. - -""" - -import re - -_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*').match -_declstringlit_match = re.compile(r'(\'[^\']*\'|"[^"]*")\s*').match -_commentclose = re.compile(r'--\s*>') -_markedsectionclose = re.compile(r']\s*]\s*>') - -# An analysis of the MS-Word extensions is available at -# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf - -_msmarkedsectionclose = re.compile(r']\s*>') - -del re - - -class ParserBase(object): - """Parser base class which provides some common support methods used - by the SGML/HTML and XHTML parsers.""" - - def __init__(self): - if self.__class__ is ParserBase: - raise RuntimeError( - "_markupbase.ParserBase must be subclassed") - - def error(self, message): - raise NotImplementedError( - "subclasses of ParserBase must override error()") - - def reset(self): - self.lineno = 1 - self.offset = 0 - - def getpos(self): - """Return current line number and offset.""" - return self.lineno, self.offset - - # Internal -- update line number and offset. This should be - # called for each piece of data exactly once, in order -- in other - # words the concatenation of all the input strings to this - # function should be exactly the entire input. - def updatepos(self, i, j): - if i >= j: - return j - rawdata = self.rawdata - nlines = rawdata.count("\n", i, j) - if nlines: - self.lineno = self.lineno + nlines - pos = rawdata.rindex("\n", i, j) # Should not fail - self.offset = j-(pos+1) - else: - self.offset = self.offset + j-i - return j - - _decl_otherchars = '' - - # Internal -- parse declaration (for use by subclasses). - def parse_declaration(self, i): - # This is some sort of declaration; in "HTML as - # deployed," this should only be the document type - # declaration (""). - # ISO 8879:1986, however, has more complex - # declaration syntax for elements in , including: - # --comment-- - # [marked section] - # name in the following list: ENTITY, DOCTYPE, ELEMENT, - # ATTLIST, NOTATION, SHORTREF, USEMAP, - # LINKTYPE, LINK, IDLINK, USELINK, SYSTEM - rawdata = self.rawdata - j = i + 2 - assert rawdata[i:j] == "": - # the empty comment - return j + 1 - if rawdata[j:j+1] in ("-", ""): - # Start of comment followed by buffer boundary, - # or just a buffer boundary. - return -1 - # A simple, practical version could look like: ((name|stringlit) S*) + '>' - n = len(rawdata) - if rawdata[j:j+2] == '--': #comment - # Locate --.*-- as the body of the comment - return self.parse_comment(i) - elif rawdata[j] == '[': #marked section - # Locate [statusWord [...arbitrary SGML...]] as the body of the marked section - # Where statusWord is one of TEMP, CDATA, IGNORE, INCLUDE, RCDATA - # Note that this is extended by Microsoft Office "Save as Web" function - # to include [if...] and [endif]. - return self.parse_marked_section(i) - else: #all other declaration elements - decltype, j = self._scan_name(j, i) - if j < 0: - return j - if decltype == "doctype": - self._decl_otherchars = '' - while j < n: - c = rawdata[j] - if c == ">": - # end of declaration syntax - data = rawdata[i+2:j] - if decltype == "doctype": - self.handle_decl(data) - else: - # According to the HTML5 specs sections "8.2.4.44 Bogus - # comment state" and "8.2.4.45 Markup declaration open - # state", a comment token should be emitted. - # Calling unknown_decl provides more flexibility though. - self.unknown_decl(data) - return j + 1 - if c in "\"'": - m = _declstringlit_match(rawdata, j) - if not m: - return -1 # incomplete - j = m.end() - elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": - name, j = self._scan_name(j, i) - elif c in self._decl_otherchars: - j = j + 1 - elif c == "[": - # this could be handled in a separate doctype parser - if decltype == "doctype": - j = self._parse_doctype_subset(j + 1, i) - elif decltype in set(["attlist", "linktype", "link", "element"]): - # must tolerate []'d groups in a content model in an element declaration - # also in data attribute specifications of attlist declaration - # also link type declaration subsets in linktype declarations - # also link attribute specification lists in link declarations - self.error("unsupported '[' char in %s declaration" % decltype) - else: - self.error("unexpected '[' char in declaration") - else: - self.error( - "unexpected %r char in declaration" % rawdata[j]) - if j < 0: - return j - return -1 # incomplete - - # Internal -- parse a marked section - # Override this to handle MS-word extension syntax content - def parse_marked_section(self, i, report=1): - rawdata= self.rawdata - assert rawdata[i:i+3] == ' ending - match= _markedsectionclose.search(rawdata, i+3) - elif sectName in set(["if", "else", "endif"]): - # look for MS Office ]> ending - match= _msmarkedsectionclose.search(rawdata, i+3) - else: - self.error('unknown status keyword %r in marked section' % rawdata[i+3:j]) - if not match: - return -1 - if report: - j = match.start(0) - self.unknown_decl(rawdata[i+3: j]) - return match.end(0) - - # Internal -- parse comment, return length or -1 if not terminated - def parse_comment(self, i, report=1): - rawdata = self.rawdata - if rawdata[i:i+4] != '| UnixStreamServer | - +-----------+ +------------------+ - | - v - +-----------+ +--------------------+ - | UDPServer |------->| UnixDatagramServer | - +-----------+ +--------------------+ - -Note that UnixDatagramServer derives from UDPServer, not from -UnixStreamServer -- the only difference between an IP and a Unix -stream server is the address family, which is simply repeated in both -unix server classes. - -Forking and threading versions of each type of server can be created -using the ForkingMixIn and ThreadingMixIn mix-in classes. For -instance, a threading UDP server class is created as follows: - - class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass - -The Mix-in class must come first, since it overrides a method defined -in UDPServer! Setting the various member variables also changes -the behavior of the underlying server mechanism. - -To implement a service, you must derive a class from -BaseRequestHandler and redefine its handle() method. You can then run -various versions of the service by combining one of the server classes -with your request handler class. - -The request handler class must be different for datagram or stream -services. This can be hidden by using the request handler -subclasses StreamRequestHandler or DatagramRequestHandler. - -Of course, you still have to use your head! - -For instance, it makes no sense to use a forking server if the service -contains state in memory that can be modified by requests (since the -modifications in the child process would never reach the initial state -kept in the parent process and passed to each child). In this case, -you can use a threading server, but you will probably have to use -locks to avoid two requests that come in nearly simultaneous to apply -conflicting changes to the server state. - -On the other hand, if you are building e.g. an HTTP server, where all -data is stored externally (e.g. in the file system), a synchronous -class will essentially render the service "deaf" while one request is -being handled -- which may be for a very long time if a client is slow -to read all the data it has requested. Here a threading or forking -server is appropriate. - -In some cases, it may be appropriate to process part of a request -synchronously, but to finish processing in a forked child depending on -the request data. This can be implemented by using a synchronous -server and doing an explicit fork in the request handler class -handle() method. - -Another approach to handling multiple simultaneous requests in an -environment that supports neither threads nor fork (or where these are -too expensive or inappropriate for the service) is to maintain an -explicit table of partially finished requests and to use select() to -decide which request to work on next (or whether to handle a new -incoming request). This is particularly important for stream services -where each client can potentially be connected for a long time (if -threads or subprocesses cannot be used). - -Future work: -- Standard classes for Sun RPC (which uses either UDP or TCP) -- Standard mix-in classes to implement various authentication - and encryption schemes -- Standard framework for select-based multiplexing - -XXX Open problems: -- What to do with out-of-band data? - -BaseServer: -- split generic "request" functionality out into BaseServer class. - Copyright (C) 2000 Luke Kenneth Casson Leighton - - example: read entries from a SQL database (requires overriding - get_request() to return a table entry from the database). - entry is processed by a RequestHandlerClass. - -""" - -# Author of the BaseServer patch: Luke Kenneth Casson Leighton - -# XXX Warning! -# There is a test suite for this module, but it cannot be run by the -# standard regression test. -# To run it manually, run Lib/test/test_socketserver.py. - -from __future__ import (absolute_import, print_function) - -__version__ = "0.4" - - -import socket -import select -import sys -import os -import errno -try: - import threading -except ImportError: - import dummy_threading as threading - -__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", - "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler", - "StreamRequestHandler","DatagramRequestHandler", - "ThreadingMixIn", "ForkingMixIn"] -if hasattr(socket, "AF_UNIX"): - __all__.extend(["UnixStreamServer","UnixDatagramServer", - "ThreadingUnixStreamServer", - "ThreadingUnixDatagramServer"]) - -def _eintr_retry(func, *args): - """restart a system call interrupted by EINTR""" - while True: - try: - return func(*args) - except OSError as e: - if e.errno != errno.EINTR: - raise - -class BaseServer(object): - - """Base class for server classes. - - Methods for the caller: - - - __init__(server_address, RequestHandlerClass) - - serve_forever(poll_interval=0.5) - - shutdown() - - handle_request() # if you do not use serve_forever() - - fileno() -> int # for select() - - Methods that may be overridden: - - - server_bind() - - server_activate() - - get_request() -> request, client_address - - handle_timeout() - - verify_request(request, client_address) - - server_close() - - process_request(request, client_address) - - shutdown_request(request) - - close_request(request) - - service_actions() - - handle_error() - - Methods for derived classes: - - - finish_request(request, client_address) - - Class variables that may be overridden by derived classes or - instances: - - - timeout - - address_family - - socket_type - - allow_reuse_address - - Instance variables: - - - RequestHandlerClass - - socket - - """ - - timeout = None - - def __init__(self, server_address, RequestHandlerClass): - """Constructor. May be extended, do not override.""" - self.server_address = server_address - self.RequestHandlerClass = RequestHandlerClass - self.__is_shut_down = threading.Event() - self.__shutdown_request = False - - def server_activate(self): - """Called by constructor to activate the server. - - May be overridden. - - """ - pass - - def serve_forever(self, poll_interval=0.5): - """Handle one request at a time until shutdown. - - Polls for shutdown every poll_interval seconds. Ignores - self.timeout. If you need to do periodic tasks, do them in - another thread. - """ - self.__is_shut_down.clear() - try: - while not self.__shutdown_request: - # XXX: Consider using another file descriptor or - # connecting to the socket to wake this up instead of - # polling. Polling reduces our responsiveness to a - # shutdown request and wastes cpu at all other times. - r, w, e = _eintr_retry(select.select, [self], [], [], - poll_interval) - if self in r: - self._handle_request_noblock() - - self.service_actions() - finally: - self.__shutdown_request = False - self.__is_shut_down.set() - - def shutdown(self): - """Stops the serve_forever loop. - - Blocks until the loop has finished. This must be called while - serve_forever() is running in another thread, or it will - deadlock. - """ - self.__shutdown_request = True - self.__is_shut_down.wait() - - def service_actions(self): - """Called by the serve_forever() loop. - - May be overridden by a subclass / Mixin to implement any code that - needs to be run during the loop. - """ - pass - - # The distinction between handling, getting, processing and - # finishing a request is fairly arbitrary. Remember: - # - # - handle_request() is the top-level call. It calls - # select, get_request(), verify_request() and process_request() - # - get_request() is different for stream or datagram sockets - # - process_request() is the place that may fork a new process - # or create a new thread to finish the request - # - finish_request() instantiates the request handler class; - # this constructor will handle the request all by itself - - def handle_request(self): - """Handle one request, possibly blocking. - - Respects self.timeout. - """ - # Support people who used socket.settimeout() to escape - # handle_request before self.timeout was available. - timeout = self.socket.gettimeout() - if timeout is None: - timeout = self.timeout - elif self.timeout is not None: - timeout = min(timeout, self.timeout) - fd_sets = _eintr_retry(select.select, [self], [], [], timeout) - if not fd_sets[0]: - self.handle_timeout() - return - self._handle_request_noblock() - - def _handle_request_noblock(self): - """Handle one request, without blocking. - - I assume that select.select has returned that the socket is - readable before this function was called, so there should be - no risk of blocking in get_request(). - """ - try: - request, client_address = self.get_request() - except socket.error: - return - if self.verify_request(request, client_address): - try: - self.process_request(request, client_address) - except: - self.handle_error(request, client_address) - self.shutdown_request(request) - - def handle_timeout(self): - """Called if no new request arrives within self.timeout. - - Overridden by ForkingMixIn. - """ - pass - - def verify_request(self, request, client_address): - """Verify the request. May be overridden. - - Return True if we should proceed with this request. - - """ - return True - - def process_request(self, request, client_address): - """Call finish_request. - - Overridden by ForkingMixIn and ThreadingMixIn. - - """ - self.finish_request(request, client_address) - self.shutdown_request(request) - - def server_close(self): - """Called to clean-up the server. - - May be overridden. - - """ - pass - - def finish_request(self, request, client_address): - """Finish one request by instantiating RequestHandlerClass.""" - self.RequestHandlerClass(request, client_address, self) - - def shutdown_request(self, request): - """Called to shutdown and close an individual request.""" - self.close_request(request) - - def close_request(self, request): - """Called to clean up an individual request.""" - pass - - def handle_error(self, request, client_address): - """Handle an error gracefully. May be overridden. - - The default is to print a traceback and continue. - - """ - print('-'*40) - print('Exception happened during processing of request from', end=' ') - print(client_address) - import traceback - traceback.print_exc() # XXX But this goes to stderr! - print('-'*40) - - -class TCPServer(BaseServer): - - """Base class for various socket-based server classes. - - Defaults to synchronous IP stream (i.e., TCP). - - Methods for the caller: - - - __init__(server_address, RequestHandlerClass, bind_and_activate=True) - - serve_forever(poll_interval=0.5) - - shutdown() - - handle_request() # if you don't use serve_forever() - - fileno() -> int # for select() - - Methods that may be overridden: - - - server_bind() - - server_activate() - - get_request() -> request, client_address - - handle_timeout() - - verify_request(request, client_address) - - process_request(request, client_address) - - shutdown_request(request) - - close_request(request) - - handle_error() - - Methods for derived classes: - - - finish_request(request, client_address) - - Class variables that may be overridden by derived classes or - instances: - - - timeout - - address_family - - socket_type - - request_queue_size (only for stream sockets) - - allow_reuse_address - - Instance variables: - - - server_address - - RequestHandlerClass - - socket - - """ - - address_family = socket.AF_INET - - socket_type = socket.SOCK_STREAM - - request_queue_size = 5 - - allow_reuse_address = False - - def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): - """Constructor. May be extended, do not override.""" - BaseServer.__init__(self, server_address, RequestHandlerClass) - self.socket = socket.socket(self.address_family, - self.socket_type) - if bind_and_activate: - self.server_bind() - self.server_activate() - - def server_bind(self): - """Called by constructor to bind the socket. - - May be overridden. - - """ - if self.allow_reuse_address: - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.bind(self.server_address) - self.server_address = self.socket.getsockname() - - def server_activate(self): - """Called by constructor to activate the server. - - May be overridden. - - """ - self.socket.listen(self.request_queue_size) - - def server_close(self): - """Called to clean-up the server. - - May be overridden. - - """ - self.socket.close() - - def fileno(self): - """Return socket file number. - - Interface required by select(). - - """ - return self.socket.fileno() - - def get_request(self): - """Get the request and client address from the socket. - - May be overridden. - - """ - return self.socket.accept() - - def shutdown_request(self, request): - """Called to shutdown and close an individual request.""" - try: - #explicitly shutdown. socket.close() merely releases - #the socket and waits for GC to perform the actual close. - request.shutdown(socket.SHUT_WR) - except socket.error: - pass #some platforms may raise ENOTCONN here - self.close_request(request) - - def close_request(self, request): - """Called to clean up an individual request.""" - request.close() - - -class UDPServer(TCPServer): - - """UDP server class.""" - - allow_reuse_address = False - - socket_type = socket.SOCK_DGRAM - - max_packet_size = 8192 - - def get_request(self): - data, client_addr = self.socket.recvfrom(self.max_packet_size) - return (data, self.socket), client_addr - - def server_activate(self): - # No need to call listen() for UDP. - pass - - def shutdown_request(self, request): - # No need to shutdown anything. - self.close_request(request) - - def close_request(self, request): - # No need to close anything. - pass - -class ForkingMixIn(object): - - """Mix-in class to handle each request in a new process.""" - - timeout = 300 - active_children = None - max_children = 40 - - def collect_children(self): - """Internal routine to wait for children that have exited.""" - if self.active_children is None: return - while len(self.active_children) >= self.max_children: - # XXX: This will wait for any child process, not just ones - # spawned by this library. This could confuse other - # libraries that expect to be able to wait for their own - # children. - try: - pid, status = os.waitpid(0, 0) - except os.error: - pid = None - if pid not in self.active_children: continue - self.active_children.remove(pid) - - # XXX: This loop runs more system calls than it ought - # to. There should be a way to put the active_children into a - # process group and then use os.waitpid(-pgid) to wait for any - # of that set, but I couldn't find a way to allocate pgids - # that couldn't collide. - for child in self.active_children: - try: - pid, status = os.waitpid(child, os.WNOHANG) - except os.error: - pid = None - if not pid: continue - try: - self.active_children.remove(pid) - except ValueError as e: - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, - self.active_children)) - - def handle_timeout(self): - """Wait for zombies after self.timeout seconds of inactivity. - - May be extended, do not override. - """ - self.collect_children() - - def service_actions(self): - """Collect the zombie child processes regularly in the ForkingMixIn. - - service_actions is called in the BaseServer's serve_forver loop. - """ - self.collect_children() - - def process_request(self, request, client_address): - """Fork a new subprocess to process the request.""" - pid = os.fork() - if pid: - # Parent process - if self.active_children is None: - self.active_children = [] - self.active_children.append(pid) - self.close_request(request) - return - else: - # Child process. - # This must never return, hence os._exit()! - try: - self.finish_request(request, client_address) - self.shutdown_request(request) - os._exit(0) - except: - try: - self.handle_error(request, client_address) - self.shutdown_request(request) - finally: - os._exit(1) - - -class ThreadingMixIn(object): - """Mix-in class to handle each request in a new thread.""" - - # Decides how threads will act upon termination of the - # main process - daemon_threads = False - - def process_request_thread(self, request, client_address): - """Same as in BaseServer but as a thread. - - In addition, exception handling is done here. - - """ - try: - self.finish_request(request, client_address) - self.shutdown_request(request) - except: - self.handle_error(request, client_address) - self.shutdown_request(request) - - def process_request(self, request, client_address): - """Start a new thread to process the request.""" - t = threading.Thread(target = self.process_request_thread, - args = (request, client_address)) - t.daemon = self.daemon_threads - t.start() - - -class ForkingUDPServer(ForkingMixIn, UDPServer): pass -class ForkingTCPServer(ForkingMixIn, TCPServer): pass - -class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass -class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass - -if hasattr(socket, 'AF_UNIX'): - - class UnixStreamServer(TCPServer): - address_family = socket.AF_UNIX - - class UnixDatagramServer(UDPServer): - address_family = socket.AF_UNIX - - class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass - - class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass - -class BaseRequestHandler(object): - - """Base class for request handler classes. - - This class is instantiated for each request to be handled. The - constructor sets the instance variables request, client_address - and server, and then calls the handle() method. To implement a - specific service, all you need to do is to derive a class which - defines a handle() method. - - The handle() method can find the request as self.request, the - client address as self.client_address, and the server (in case it - needs access to per-server information) as self.server. Since a - separate instance is created for each request, the handle() method - can define arbitrary other instance variariables. - - """ - - def __init__(self, request, client_address, server): - self.request = request - self.client_address = client_address - self.server = server - self.setup() - try: - self.handle() - finally: - self.finish() - - def setup(self): - pass - - def handle(self): - pass - - def finish(self): - pass - - -# The following two classes make it possible to use the same service -# class for stream or datagram servers. -# Each class sets up these instance variables: -# - rfile: a file object from which receives the request is read -# - wfile: a file object to which the reply is written -# When the handle() method returns, wfile is flushed properly - - -class StreamRequestHandler(BaseRequestHandler): - - """Define self.rfile and self.wfile for stream sockets.""" - - # Default buffer sizes for rfile, wfile. - # We default rfile to buffered because otherwise it could be - # really slow for large data (a getc() call per byte); we make - # wfile unbuffered because (a) often after a write() we want to - # read and we need to flush the line; (b) big writes to unbuffered - # files are typically optimized by stdio even when big reads - # aren't. - rbufsize = -1 - wbufsize = 0 - - # A timeout to apply to the request socket, if not None. - timeout = None - - # Disable nagle algorithm for this socket, if True. - # Use only when wbufsize != 0, to avoid small packets. - disable_nagle_algorithm = False - - def setup(self): - self.connection = self.request - if self.timeout is not None: - self.connection.settimeout(self.timeout) - if self.disable_nagle_algorithm: - self.connection.setsockopt(socket.IPPROTO_TCP, - socket.TCP_NODELAY, True) - self.rfile = self.connection.makefile('rb', self.rbufsize) - self.wfile = self.connection.makefile('wb', self.wbufsize) - - def finish(self): - if not self.wfile.closed: - try: - self.wfile.flush() - except socket.error: - # An final socket error may have occurred here, such as - # the local error ECONNABORTED. - pass - self.wfile.close() - self.rfile.close() - - -class DatagramRequestHandler(BaseRequestHandler): - - # XXX Regrettably, I cannot get this working on Linux; - # s.recvfrom() doesn't return a meaningful client address. - - """Define self.rfile and self.wfile for datagram sockets.""" - - def setup(self): - from io import BytesIO - self.packet, self.socket = self.request - self.rfile = BytesIO(self.packet) - self.wfile = BytesIO() - - def finish(self): - self.socket.sendto(self.wfile.getvalue(), self.client_address) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -""" -test package backported for python-future. - -Its primary purpose is to allow use of "import test.support" for running -the Python standard library unit tests using the new Python 3 stdlib -import location. - -Python 3 renamed test.test_support to test.support. -""" diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/pystone.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/pystone.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/pystone.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/pystone.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,272 +0,0 @@ -#!/usr/bin/env python3 - -""" -"PYSTONE" Benchmark Program - -Version: Python/1.1 (corresponds to C/1.1 plus 2 Pystone fixes) - -Author: Reinhold P. Weicker, CACM Vol 27, No 10, 10/84 pg. 1013. - - Translated from ADA to C by Rick Richardson. - Every method to preserve ADA-likeness has been used, - at the expense of C-ness. - - Translated from C to Python by Guido van Rossum. - -Version History: - - Version 1.1 corrects two bugs in version 1.0: - - First, it leaked memory: in Proc1(), NextRecord ends - up having a pointer to itself. I have corrected this - by zapping NextRecord.PtrComp at the end of Proc1(). - - Second, Proc3() used the operator != to compare a - record to None. This is rather inefficient and not - true to the intention of the original benchmark (where - a pointer comparison to None is intended; the != - operator attempts to find a method __cmp__ to do value - comparison of the record). Version 1.1 runs 5-10 - percent faster than version 1.0, so benchmark figures - of different versions can't be compared directly. - -""" - -from __future__ import print_function - -from time import clock - -LOOPS = 50000 - -__version__ = "1.1" - -[Ident1, Ident2, Ident3, Ident4, Ident5] = range(1, 6) - -class Record(object): - - def __init__(self, PtrComp = None, Discr = 0, EnumComp = 0, - IntComp = 0, StringComp = 0): - self.PtrComp = PtrComp - self.Discr = Discr - self.EnumComp = EnumComp - self.IntComp = IntComp - self.StringComp = StringComp - - def copy(self): - return Record(self.PtrComp, self.Discr, self.EnumComp, - self.IntComp, self.StringComp) - -TRUE = 1 -FALSE = 0 - -def main(loops=LOOPS): - benchtime, stones = pystones(loops) - print("Pystone(%s) time for %d passes = %g" % \ - (__version__, loops, benchtime)) - print("This machine benchmarks at %g pystones/second" % stones) - - -def pystones(loops=LOOPS): - return Proc0(loops) - -IntGlob = 0 -BoolGlob = FALSE -Char1Glob = '\0' -Char2Glob = '\0' -Array1Glob = [0]*51 -Array2Glob = [x[:] for x in [Array1Glob]*51] -PtrGlb = None -PtrGlbNext = None - -def Proc0(loops=LOOPS): - global IntGlob - global BoolGlob - global Char1Glob - global Char2Glob - global Array1Glob - global Array2Glob - global PtrGlb - global PtrGlbNext - - starttime = clock() - for i in range(loops): - pass - nulltime = clock() - starttime - - PtrGlbNext = Record() - PtrGlb = Record() - PtrGlb.PtrComp = PtrGlbNext - PtrGlb.Discr = Ident1 - PtrGlb.EnumComp = Ident3 - PtrGlb.IntComp = 40 - PtrGlb.StringComp = "DHRYSTONE PROGRAM, SOME STRING" - String1Loc = "DHRYSTONE PROGRAM, 1'ST STRING" - Array2Glob[8][7] = 10 - - starttime = clock() - - for i in range(loops): - Proc5() - Proc4() - IntLoc1 = 2 - IntLoc2 = 3 - String2Loc = "DHRYSTONE PROGRAM, 2'ND STRING" - EnumLoc = Ident2 - BoolGlob = not Func2(String1Loc, String2Loc) - while IntLoc1 < IntLoc2: - IntLoc3 = 5 * IntLoc1 - IntLoc2 - IntLoc3 = Proc7(IntLoc1, IntLoc2) - IntLoc1 = IntLoc1 + 1 - Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3) - PtrGlb = Proc1(PtrGlb) - CharIndex = 'A' - while CharIndex <= Char2Glob: - if EnumLoc == Func1(CharIndex, 'C'): - EnumLoc = Proc6(Ident1) - CharIndex = chr(ord(CharIndex)+1) - IntLoc3 = IntLoc2 * IntLoc1 - IntLoc2 = IntLoc3 / IntLoc1 - IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1 - IntLoc1 = Proc2(IntLoc1) - - benchtime = clock() - starttime - nulltime - if benchtime == 0.0: - loopsPerBenchtime = 0.0 - else: - loopsPerBenchtime = (loops / benchtime) - return benchtime, loopsPerBenchtime - -def Proc1(PtrParIn): - PtrParIn.PtrComp = NextRecord = PtrGlb.copy() - PtrParIn.IntComp = 5 - NextRecord.IntComp = PtrParIn.IntComp - NextRecord.PtrComp = PtrParIn.PtrComp - NextRecord.PtrComp = Proc3(NextRecord.PtrComp) - if NextRecord.Discr == Ident1: - NextRecord.IntComp = 6 - NextRecord.EnumComp = Proc6(PtrParIn.EnumComp) - NextRecord.PtrComp = PtrGlb.PtrComp - NextRecord.IntComp = Proc7(NextRecord.IntComp, 10) - else: - PtrParIn = NextRecord.copy() - NextRecord.PtrComp = None - return PtrParIn - -def Proc2(IntParIO): - IntLoc = IntParIO + 10 - while 1: - if Char1Glob == 'A': - IntLoc = IntLoc - 1 - IntParIO = IntLoc - IntGlob - EnumLoc = Ident1 - if EnumLoc == Ident1: - break - return IntParIO - -def Proc3(PtrParOut): - global IntGlob - - if PtrGlb is not None: - PtrParOut = PtrGlb.PtrComp - else: - IntGlob = 100 - PtrGlb.IntComp = Proc7(10, IntGlob) - return PtrParOut - -def Proc4(): - global Char2Glob - - BoolLoc = Char1Glob == 'A' - BoolLoc = BoolLoc or BoolGlob - Char2Glob = 'B' - -def Proc5(): - global Char1Glob - global BoolGlob - - Char1Glob = 'A' - BoolGlob = FALSE - -def Proc6(EnumParIn): - EnumParOut = EnumParIn - if not Func3(EnumParIn): - EnumParOut = Ident4 - if EnumParIn == Ident1: - EnumParOut = Ident1 - elif EnumParIn == Ident2: - if IntGlob > 100: - EnumParOut = Ident1 - else: - EnumParOut = Ident4 - elif EnumParIn == Ident3: - EnumParOut = Ident2 - elif EnumParIn == Ident4: - pass - elif EnumParIn == Ident5: - EnumParOut = Ident3 - return EnumParOut - -def Proc7(IntParI1, IntParI2): - IntLoc = IntParI1 + 2 - IntParOut = IntParI2 + IntLoc - return IntParOut - -def Proc8(Array1Par, Array2Par, IntParI1, IntParI2): - global IntGlob - - IntLoc = IntParI1 + 5 - Array1Par[IntLoc] = IntParI2 - Array1Par[IntLoc+1] = Array1Par[IntLoc] - Array1Par[IntLoc+30] = IntLoc - for IntIndex in range(IntLoc, IntLoc+2): - Array2Par[IntLoc][IntIndex] = IntLoc - Array2Par[IntLoc][IntLoc-1] = Array2Par[IntLoc][IntLoc-1] + 1 - Array2Par[IntLoc+20][IntLoc] = Array1Par[IntLoc] - IntGlob = 5 - -def Func1(CharPar1, CharPar2): - CharLoc1 = CharPar1 - CharLoc2 = CharLoc1 - if CharLoc2 != CharPar2: - return Ident1 - else: - return Ident2 - -def Func2(StrParI1, StrParI2): - IntLoc = 1 - while IntLoc <= 1: - if Func1(StrParI1[IntLoc], StrParI2[IntLoc+1]) == Ident1: - CharLoc = 'A' - IntLoc = IntLoc + 1 - if CharLoc >= 'W' and CharLoc <= 'Z': - IntLoc = 7 - if CharLoc == 'X': - return TRUE - else: - if StrParI1 > StrParI2: - IntLoc = IntLoc + 7 - return TRUE - else: - return FALSE - -def Func3(EnumParIn): - EnumLoc = EnumParIn - if EnumLoc == Ident3: return TRUE - return FALSE - -if __name__ == '__main__': - import sys - def error(msg): - print(msg, end=' ', file=sys.stderr) - print("usage: %s [number_of_loops]" % sys.argv[0], file=sys.stderr) - sys.exit(100) - nargs = len(sys.argv) - 1 - if nargs > 1: - error("%d arguments are too many;" % nargs) - elif nargs == 1: - try: loops = int(sys.argv[1]) - except ValueError: - error("Invalid argument %r;" % sys.argv[1]) - else: - loops = LOOPS - main(loops) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/ssl_servers.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/ssl_servers.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/ssl_servers.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/ssl_servers.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,207 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import filter, str -from future import utils -import os -import sys -import ssl -import pprint -import socket -from future.backports.urllib import parse as urllib_parse -from future.backports.http.server import (HTTPServer as _HTTPServer, - SimpleHTTPRequestHandler, BaseHTTPRequestHandler) -from future.backports.test import support -threading = support.import_module("threading") - -here = os.path.dirname(__file__) - -HOST = support.HOST -CERTFILE = os.path.join(here, 'keycert.pem') - -# This one's based on HTTPServer, which is based on SocketServer - -class HTTPSServer(_HTTPServer): - - def __init__(self, server_address, handler_class, context): - _HTTPServer.__init__(self, server_address, handler_class) - self.context = context - - def __str__(self): - return ('<%s %s:%s>' % - (self.__class__.__name__, - self.server_name, - self.server_port)) - - def get_request(self): - # override this to wrap socket with SSL - try: - sock, addr = self.socket.accept() - sslconn = self.context.wrap_socket(sock, server_side=True) - except socket.error as e: - # socket errors are silenced by the caller, print them here - if support.verbose: - sys.stderr.write("Got an error:\n%s\n" % e) - raise - return sslconn, addr - -class RootedHTTPRequestHandler(SimpleHTTPRequestHandler): - # need to override translate_path to get a known root, - # instead of using os.curdir, since the test could be - # run from anywhere - - server_version = "TestHTTPS/1.0" - root = here - # Avoid hanging when a request gets interrupted by the client - timeout = 5 - - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - # abandon query parameters - path = urllib.parse.urlparse(path)[2] - path = os.path.normpath(urllib.parse.unquote(path)) - words = path.split('/') - words = filter(None, words) - path = self.root - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - path = os.path.join(path, word) - return path - - def log_message(self, format, *args): - # we override this to suppress logging unless "verbose" - if support.verbose: - sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" % - (self.server.server_address, - self.server.server_port, - self.request.cipher(), - self.log_date_time_string(), - format%args)) - - -class StatsRequestHandler(BaseHTTPRequestHandler): - """Example HTTP request handler which returns SSL statistics on GET - requests. - """ - - server_version = "StatsHTTPS/1.0" - - def do_GET(self, send_body=True): - """Serve a GET request.""" - sock = self.rfile.raw._sock - context = sock.context - stats = { - 'session_cache': context.session_stats(), - 'cipher': sock.cipher(), - 'compression': sock.compression(), - } - body = pprint.pformat(stats) - body = body.encode('utf-8') - self.send_response(200) - self.send_header("Content-type", "text/plain; charset=utf-8") - self.send_header("Content-Length", str(len(body))) - self.end_headers() - if send_body: - self.wfile.write(body) - - def do_HEAD(self): - """Serve a HEAD request.""" - self.do_GET(send_body=False) - - def log_request(self, format, *args): - if support.verbose: - BaseHTTPRequestHandler.log_request(self, format, *args) - - -class HTTPSServerThread(threading.Thread): - - def __init__(self, context, host=HOST, handler_class=None): - self.flag = None - self.server = HTTPSServer((host, 0), - handler_class or RootedHTTPRequestHandler, - context) - self.port = self.server.server_port - threading.Thread.__init__(self) - self.daemon = True - - def __str__(self): - return "<%s %s>" % (self.__class__.__name__, self.server) - - def start(self, flag=None): - self.flag = flag - threading.Thread.start(self) - - def run(self): - if self.flag: - self.flag.set() - try: - self.server.serve_forever(0.05) - finally: - self.server.server_close() - - def stop(self): - self.server.shutdown() - - -def make_https_server(case, certfile=CERTFILE, host=HOST, handler_class=None): - # we assume the certfile contains both private key and certificate - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.load_cert_chain(certfile) - server = HTTPSServerThread(context, host, handler_class) - flag = threading.Event() - server.start(flag) - flag.wait() - def cleanup(): - if support.verbose: - sys.stdout.write('stopping HTTPS server\n') - server.stop() - if support.verbose: - sys.stdout.write('joining HTTPS thread\n') - server.join() - case.addCleanup(cleanup) - return server - - -if __name__ == "__main__": - import argparse - parser = argparse.ArgumentParser( - description='Run a test HTTPS server. ' - 'By default, the current directory is served.') - parser.add_argument('-p', '--port', type=int, default=4433, - help='port to listen on (default: %(default)s)') - parser.add_argument('-q', '--quiet', dest='verbose', default=True, - action='store_false', help='be less verbose') - parser.add_argument('-s', '--stats', dest='use_stats_handler', default=False, - action='store_true', help='always return stats page') - parser.add_argument('--curve-name', dest='curve_name', type=str, - action='store', - help='curve name for EC-based Diffie-Hellman') - parser.add_argument('--dh', dest='dh_file', type=str, action='store', - help='PEM file containing DH parameters') - args = parser.parse_args() - - support.verbose = args.verbose - if args.use_stats_handler: - handler_class = StatsRequestHandler - else: - handler_class = RootedHTTPRequestHandler - if utils.PY2: - handler_class.root = os.getcwdu() - else: - handler_class.root = os.getcwd() - context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - context.load_cert_chain(CERTFILE) - if args.curve_name: - context.set_ecdh_curve(args.curve_name) - if args.dh_file: - context.load_dh_params(args.dh_file) - - server = HTTPSServer(("", args.port), handler_class, context) - if args.verbose: - print("Listening on https://localhost:{0.port}".format(args)) - server.serve_forever(0.1) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/support.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/support.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/test/support.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/test/support.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2048 +0,0 @@ -# -*- coding: utf-8 -*- -"""Supporting definitions for the Python regression tests. - -Backported for python-future from Python 3.3 test/support.py. -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future import utils -from future.builtins import str, range, open, int, map, list - -import contextlib -import errno -import functools -import gc -import socket -import sys -import os -import platform -import shutil -import warnings -import unittest -# For Python 2.6 compatibility: -if not hasattr(unittest, 'skip'): - import unittest2 as unittest - -import importlib -# import collections.abc # not present on Py2.7 -import re -import subprocess -import imp -import time -try: - import sysconfig -except ImportError: - # sysconfig is not available on Python 2.6. Try using distutils.sysconfig instead: - from distutils import sysconfig -import fnmatch -import logging.handlers -import struct -import tempfile - -try: - if utils.PY3: - import _thread, threading - else: - import thread as _thread, threading -except ImportError: - _thread = None - threading = None -try: - import multiprocessing.process -except ImportError: - multiprocessing = None - -try: - import zlib -except ImportError: - zlib = None - -try: - import gzip -except ImportError: - gzip = None - -try: - import bz2 -except ImportError: - bz2 = None - -try: - import lzma -except ImportError: - lzma = None - -__all__ = [ - "Error", "TestFailed", "ResourceDenied", "import_module", "verbose", - "use_resources", "max_memuse", "record_original_stdout", - "get_original_stdout", "unload", "unlink", "rmtree", "forget", - "is_resource_enabled", "requires", "requires_freebsd_version", - "requires_linux_version", "requires_mac_ver", "find_unused_port", - "bind_port", "IPV6_ENABLED", "is_jython", "TESTFN", "HOST", "SAVEDCWD", - "temp_cwd", "findfile", "create_empty_file", "sortdict", - "check_syntax_error", "open_urlresource", "check_warnings", "CleanImport", - "EnvironmentVarGuard", "TransientResource", "captured_stdout", - "captured_stdin", "captured_stderr", "time_out", "socket_peer_reset", - "ioerror_peer_reset", "run_with_locale", 'temp_umask', - "transient_internet", "set_memlimit", "bigmemtest", "bigaddrspacetest", - "BasicTestRunner", "run_unittest", "run_doctest", "threading_setup", - "threading_cleanup", "reap_children", "cpython_only", "check_impl_detail", - "get_attribute", "swap_item", "swap_attr", "requires_IEEE_754", - "TestHandler", "Matcher", "can_symlink", "skip_unless_symlink", - "skip_unless_xattr", "import_fresh_module", "requires_zlib", - "PIPE_MAX_SIZE", "failfast", "anticipate_failure", "run_with_tz", - "requires_gzip", "requires_bz2", "requires_lzma", "suppress_crash_popup", - ] - -class Error(Exception): - """Base class for regression test exceptions.""" - -class TestFailed(Error): - """Test failed.""" - -class ResourceDenied(unittest.SkipTest): - """Test skipped because it requested a disallowed resource. - - This is raised when a test calls requires() for a resource that - has not be enabled. It is used to distinguish between expected - and unexpected skips. - """ - -@contextlib.contextmanager -def _ignore_deprecated_imports(ignore=True): - """Context manager to suppress package and module deprecation - warnings when importing them. - - If ignore is False, this context manager has no effect.""" - if ignore: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", ".+ (module|package)", - DeprecationWarning) - yield - else: - yield - - -def import_module(name, deprecated=False): - """Import and return the module to be tested, raising SkipTest if - it is not available. - - If deprecated is True, any module or package deprecation messages - will be suppressed.""" - with _ignore_deprecated_imports(deprecated): - try: - return importlib.import_module(name) - except ImportError as msg: - raise unittest.SkipTest(str(msg)) - - -def _save_and_remove_module(name, orig_modules): - """Helper function to save and remove a module from sys.modules - - Raise ImportError if the module can't be imported. - """ - # try to import the module and raise an error if it can't be imported - if name not in sys.modules: - __import__(name) - del sys.modules[name] - for modname in list(sys.modules): - if modname == name or modname.startswith(name + '.'): - orig_modules[modname] = sys.modules[modname] - del sys.modules[modname] - -def _save_and_block_module(name, orig_modules): - """Helper function to save and block a module in sys.modules - - Return True if the module was in sys.modules, False otherwise. - """ - saved = True - try: - orig_modules[name] = sys.modules[name] - except KeyError: - saved = False - sys.modules[name] = None - return saved - - -def anticipate_failure(condition): - """Decorator to mark a test that is known to be broken in some cases - - Any use of this decorator should have a comment identifying the - associated tracker issue. - """ - if condition: - return unittest.expectedFailure - return lambda f: f - - -def import_fresh_module(name, fresh=(), blocked=(), deprecated=False): - """Import and return a module, deliberately bypassing sys.modules. - This function imports and returns a fresh copy of the named Python module - by removing the named module from sys.modules before doing the import. - Note that unlike reload, the original module is not affected by - this operation. - - *fresh* is an iterable of additional module names that are also removed - from the sys.modules cache before doing the import. - - *blocked* is an iterable of module names that are replaced with None - in the module cache during the import to ensure that attempts to import - them raise ImportError. - - The named module and any modules named in the *fresh* and *blocked* - parameters are saved before starting the import and then reinserted into - sys.modules when the fresh import is complete. - - Module and package deprecation messages are suppressed during this import - if *deprecated* is True. - - This function will raise ImportError if the named module cannot be - imported. - - If deprecated is True, any module or package deprecation messages - will be suppressed. - """ - # NOTE: test_heapq, test_json and test_warnings include extra sanity checks - # to make sure that this utility function is working as expected - with _ignore_deprecated_imports(deprecated): - # Keep track of modules saved for later restoration as well - # as those which just need a blocking entry removed - orig_modules = {} - names_to_remove = [] - _save_and_remove_module(name, orig_modules) - try: - for fresh_name in fresh: - _save_and_remove_module(fresh_name, orig_modules) - for blocked_name in blocked: - if not _save_and_block_module(blocked_name, orig_modules): - names_to_remove.append(blocked_name) - fresh_module = importlib.import_module(name) - except ImportError: - fresh_module = None - finally: - for orig_name, module in orig_modules.items(): - sys.modules[orig_name] = module - for name_to_remove in names_to_remove: - del sys.modules[name_to_remove] - return fresh_module - - -def get_attribute(obj, name): - """Get an attribute, raising SkipTest if AttributeError is raised.""" - try: - attribute = getattr(obj, name) - except AttributeError: - raise unittest.SkipTest("object %r has no attribute %r" % (obj, name)) - else: - return attribute - -verbose = 1 # Flag set to 0 by regrtest.py -use_resources = None # Flag set to [] by regrtest.py -max_memuse = 0 # Disable bigmem tests (they will still be run with - # small sizes, to make sure they work.) -real_max_memuse = 0 -failfast = False -match_tests = None - -# _original_stdout is meant to hold stdout at the time regrtest began. -# This may be "the real" stdout, or IDLE's emulation of stdout, or whatever. -# The point is to have some flavor of stdout the user can actually see. -_original_stdout = None -def record_original_stdout(stdout): - global _original_stdout - _original_stdout = stdout - -def get_original_stdout(): - return _original_stdout or sys.stdout - -def unload(name): - try: - del sys.modules[name] - except KeyError: - pass - -if sys.platform.startswith("win"): - def _waitfor(func, pathname, waitall=False): - # Perform the operation - func(pathname) - # Now setup the wait loop - if waitall: - dirname = pathname - else: - dirname, name = os.path.split(pathname) - dirname = dirname or '.' - # Check for `pathname` to be removed from the filesystem. - # The exponential backoff of the timeout amounts to a total - # of ~1 second after which the deletion is probably an error - # anyway. - # Testing on a i7@4.3GHz shows that usually only 1 iteration is - # required when contention occurs. - timeout = 0.001 - while timeout < 1.0: - # Note we are only testing for the existence of the file(s) in - # the contents of the directory regardless of any security or - # access rights. If we have made it this far, we have sufficient - # permissions to do that much using Python's equivalent of the - # Windows API FindFirstFile. - # Other Windows APIs can fail or give incorrect results when - # dealing with files that are pending deletion. - L = os.listdir(dirname) - if not (L if waitall else name in L): - return - # Increase the timeout and try again - time.sleep(timeout) - timeout *= 2 - warnings.warn('tests may fail, delete still pending for ' + pathname, - RuntimeWarning, stacklevel=4) - - def _unlink(filename): - _waitfor(os.unlink, filename) - - def _rmdir(dirname): - _waitfor(os.rmdir, dirname) - - def _rmtree(path): - def _rmtree_inner(path): - for name in os.listdir(path): - fullname = os.path.join(path, name) - if os.path.isdir(fullname): - _waitfor(_rmtree_inner, fullname, waitall=True) - os.rmdir(fullname) - else: - os.unlink(fullname) - _waitfor(_rmtree_inner, path, waitall=True) - _waitfor(os.rmdir, path) -else: - _unlink = os.unlink - _rmdir = os.rmdir - _rmtree = shutil.rmtree - -def unlink(filename): - try: - _unlink(filename) - except OSError as error: - # The filename need not exist. - if error.errno not in (errno.ENOENT, errno.ENOTDIR): - raise - -def rmdir(dirname): - try: - _rmdir(dirname) - except OSError as error: - # The directory need not exist. - if error.errno != errno.ENOENT: - raise - -def rmtree(path): - try: - _rmtree(path) - except OSError as error: - if error.errno != errno.ENOENT: - raise - -def make_legacy_pyc(source): - """Move a PEP 3147 pyc/pyo file to its legacy pyc/pyo location. - - The choice of .pyc or .pyo extension is done based on the __debug__ flag - value. - - :param source: The file system path to the source file. The source file - does not need to exist, however the PEP 3147 pyc file must exist. - :return: The file system path to the legacy pyc file. - """ - pyc_file = imp.cache_from_source(source) - up_one = os.path.dirname(os.path.abspath(source)) - legacy_pyc = os.path.join(up_one, source + ('c' if __debug__ else 'o')) - os.rename(pyc_file, legacy_pyc) - return legacy_pyc - -def forget(modname): - """'Forget' a module was ever imported. - - This removes the module from sys.modules and deletes any PEP 3147 or - legacy .pyc and .pyo files. - """ - unload(modname) - for dirname in sys.path: - source = os.path.join(dirname, modname + '.py') - # It doesn't matter if they exist or not, unlink all possible - # combinations of PEP 3147 and legacy pyc and pyo files. - unlink(source + 'c') - unlink(source + 'o') - unlink(imp.cache_from_source(source, debug_override=True)) - unlink(imp.cache_from_source(source, debug_override=False)) - -# On some platforms, should not run gui test even if it is allowed -# in `use_resources'. -if sys.platform.startswith('win'): - import ctypes - import ctypes.wintypes - def _is_gui_available(): - UOI_FLAGS = 1 - WSF_VISIBLE = 0x0001 - class USEROBJECTFLAGS(ctypes.Structure): - _fields_ = [("fInherit", ctypes.wintypes.BOOL), - ("fReserved", ctypes.wintypes.BOOL), - ("dwFlags", ctypes.wintypes.DWORD)] - dll = ctypes.windll.user32 - h = dll.GetProcessWindowStation() - if not h: - raise ctypes.WinError() - uof = USEROBJECTFLAGS() - needed = ctypes.wintypes.DWORD() - res = dll.GetUserObjectInformationW(h, - UOI_FLAGS, - ctypes.byref(uof), - ctypes.sizeof(uof), - ctypes.byref(needed)) - if not res: - raise ctypes.WinError() - return bool(uof.dwFlags & WSF_VISIBLE) -else: - def _is_gui_available(): - return True - -def is_resource_enabled(resource): - """Test whether a resource is enabled. Known resources are set by - regrtest.py.""" - return use_resources is not None and resource in use_resources - -def requires(resource, msg=None): - """Raise ResourceDenied if the specified resource is not available. - - If the caller's module is __main__ then automatically return True. The - possibility of False being returned occurs when regrtest.py is - executing. - """ - if resource == 'gui' and not _is_gui_available(): - raise unittest.SkipTest("Cannot use the 'gui' resource") - # see if the caller's module is __main__ - if so, treat as if - # the resource was set - if sys._getframe(1).f_globals.get("__name__") == "__main__": - return - if not is_resource_enabled(resource): - if msg is None: - msg = "Use of the %r resource not enabled" % resource - raise ResourceDenied(msg) - -def _requires_unix_version(sysname, min_version): - """Decorator raising SkipTest if the OS is `sysname` and the version is less - than `min_version`. - - For example, @_requires_unix_version('FreeBSD', (7, 2)) raises SkipTest if - the FreeBSD version is less than 7.2. - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kw): - if platform.system() == sysname: - version_txt = platform.release().split('-', 1)[0] - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - pass - else: - if version < min_version: - min_version_txt = '.'.join(map(str, min_version)) - raise unittest.SkipTest( - "%s version %s or higher required, not %s" - % (sysname, min_version_txt, version_txt)) - return func(*args, **kw) - wrapper.min_version = min_version - return wrapper - return decorator - -def requires_freebsd_version(*min_version): - """Decorator raising SkipTest if the OS is FreeBSD and the FreeBSD version is - less than `min_version`. - - For example, @requires_freebsd_version(7, 2) raises SkipTest if the FreeBSD - version is less than 7.2. - """ - return _requires_unix_version('FreeBSD', min_version) - -def requires_linux_version(*min_version): - """Decorator raising SkipTest if the OS is Linux and the Linux version is - less than `min_version`. - - For example, @requires_linux_version(2, 6, 32) raises SkipTest if the Linux - version is less than 2.6.32. - """ - return _requires_unix_version('Linux', min_version) - -def requires_mac_ver(*min_version): - """Decorator raising SkipTest if the OS is Mac OS X and the OS X - version if less than min_version. - - For example, @requires_mac_ver(10, 5) raises SkipTest if the OS X version - is lesser than 10.5. - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kw): - if sys.platform == 'darwin': - version_txt = platform.mac_ver()[0] - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - pass - else: - if version < min_version: - min_version_txt = '.'.join(map(str, min_version)) - raise unittest.SkipTest( - "Mac OS X %s or higher required, not %s" - % (min_version_txt, version_txt)) - return func(*args, **kw) - wrapper.min_version = min_version - return wrapper - return decorator - -# Don't use "localhost", since resolving it uses the DNS under recent -# Windows versions (see issue #18792). -HOST = "127.0.0.1" -HOSTv6 = "::1" - - -def find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM): - """Returns an unused port that should be suitable for binding. This is - achieved by creating a temporary socket with the same family and type as - the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to - the specified host address (defaults to 0.0.0.0) with the port set to 0, - eliciting an unused ephemeral port from the OS. The temporary socket is - then closed and deleted, and the ephemeral port is returned. - - Either this method or bind_port() should be used for any tests where a - server socket needs to be bound to a particular port for the duration of - the test. Which one to use depends on whether the calling code is creating - a python socket, or if an unused port needs to be provided in a constructor - or passed to an external program (i.e. the -accept argument to openssl's - s_server mode). Always prefer bind_port() over find_unused_port() where - possible. Hard coded ports should *NEVER* be used. As soon as a server - socket is bound to a hard coded port, the ability to run multiple instances - of the test simultaneously on the same host is compromised, which makes the - test a ticking time bomb in a buildbot environment. On Unix buildbots, this - may simply manifest as a failed test, which can be recovered from without - intervention in most cases, but on Windows, the entire python process can - completely and utterly wedge, requiring someone to log in to the buildbot - and manually kill the affected process. - - (This is easy to reproduce on Windows, unfortunately, and can be traced to - the SO_REUSEADDR socket option having different semantics on Windows versus - Unix/Linux. On Unix, you can't have two AF_INET SOCK_STREAM sockets bind, - listen and then accept connections on identical host/ports. An EADDRINUSE - socket.error will be raised at some point (depending on the platform and - the order bind and listen were called on each socket). - - However, on Windows, if SO_REUSEADDR is set on the sockets, no EADDRINUSE - will ever be raised when attempting to bind two identical host/ports. When - accept() is called on each socket, the second caller's process will steal - the port from the first caller, leaving them both in an awkwardly wedged - state where they'll no longer respond to any signals or graceful kills, and - must be forcibly killed via OpenProcess()/TerminateProcess(). - - The solution on Windows is to use the SO_EXCLUSIVEADDRUSE socket option - instead of SO_REUSEADDR, which effectively affords the same semantics as - SO_REUSEADDR on Unix. Given the propensity of Unix developers in the Open - Source world compared to Windows ones, this is a common mistake. A quick - look over OpenSSL's 0.9.8g source shows that they use SO_REUSEADDR when - openssl.exe is called with the 's_server' option, for example. See - http://bugs.python.org/issue2550 for more info. The following site also - has a very thorough description about the implications of both REUSEADDR - and EXCLUSIVEADDRUSE on Windows: - http://msdn2.microsoft.com/en-us/library/ms740621(VS.85).aspx) - - XXX: although this approach is a vast improvement on previous attempts to - elicit unused ports, it rests heavily on the assumption that the ephemeral - port returned to us by the OS won't immediately be dished back out to some - other process when we close and delete our temporary socket but before our - calling code has a chance to bind the returned port. We can deal with this - issue if/when we come across it. - """ - - tempsock = socket.socket(family, socktype) - port = bind_port(tempsock) - tempsock.close() - del tempsock - return port - -def bind_port(sock, host=HOST): - """Bind the socket to a free port and return the port number. Relies on - ephemeral ports in order to ensure we are using an unbound port. This is - important as many tests may be running simultaneously, especially in a - buildbot environment. This method raises an exception if the sock.family - is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR - or SO_REUSEPORT set on it. Tests should *never* set these socket options - for TCP/IP sockets. The only case for setting these options is testing - multicasting via multiple UDP sockets. - - Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e. - on Windows), it will be set on the socket. This will prevent anyone else - from bind()'ing to our host/port for the duration of the test. - """ - - if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM: - if hasattr(socket, 'SO_REUSEADDR'): - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1: - raise TestFailed("tests should never set the SO_REUSEADDR " \ - "socket option on TCP/IP sockets!") - if hasattr(socket, 'SO_REUSEPORT'): - try: - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1: - raise TestFailed("tests should never set the SO_REUSEPORT " \ - "socket option on TCP/IP sockets!") - except socket.error: - # Python's socket module was compiled using modern headers - # thus defining SO_REUSEPORT but this process is running - # under an older kernel that does not support SO_REUSEPORT. - pass - if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'): - sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) - - sock.bind((host, 0)) - port = sock.getsockname()[1] - return port - -def _is_ipv6_enabled(): - """Check whether IPv6 is enabled on this host.""" - if socket.has_ipv6: - sock = None - try: - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - sock.bind(('::1', 0)) - return True - except (socket.error, socket.gaierror): - pass - finally: - if sock: - sock.close() - return False - -IPV6_ENABLED = _is_ipv6_enabled() - - -# A constant likely larger than the underlying OS pipe buffer size, to -# make writes blocking. -# Windows limit seems to be around 512 B, and many Unix kernels have a -# 64 KiB pipe buffer size or 16 * PAGE_SIZE: take a few megs to be sure. -# (see issue #17835 for a discussion of this number). -PIPE_MAX_SIZE = 4 * 1024 * 1024 + 1 - -# A constant likely larger than the underlying OS socket buffer size, to make -# writes blocking. -# The socket buffer sizes can usually be tuned system-wide (e.g. through sysctl -# on Linux), or on a per-socket basis (SO_SNDBUF/SO_RCVBUF). See issue #18643 -# for a discussion of this number). -SOCK_MAX_SIZE = 16 * 1024 * 1024 + 1 - -# # decorator for skipping tests on non-IEEE 754 platforms -# requires_IEEE_754 = unittest.skipUnless( -# float.__getformat__("double").startswith("IEEE"), -# "test requires IEEE 754 doubles") - -requires_zlib = unittest.skipUnless(zlib, 'requires zlib') - -requires_bz2 = unittest.skipUnless(bz2, 'requires bz2') - -requires_lzma = unittest.skipUnless(lzma, 'requires lzma') - -is_jython = sys.platform.startswith('java') - -# Filename used for testing -if os.name == 'java': - # Jython disallows @ in module names - TESTFN = '$test' -else: - TESTFN = '@test' - -# Disambiguate TESTFN for parallel testing, while letting it remain a valid -# module name. -TESTFN = "{0}_{1}_tmp".format(TESTFN, os.getpid()) - -# # FS_NONASCII: non-ASCII character encodable by os.fsencode(), -# # or None if there is no such character. -# FS_NONASCII = None -# for character in ( -# # First try printable and common characters to have a readable filename. -# # For each character, the encoding list are just example of encodings able -# # to encode the character (the list is not exhaustive). -# -# # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 -# '\u00E6', -# # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 -# '\u0130', -# # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 -# '\u0141', -# # U+03C6 (Greek Small Letter Phi): cp1253 -# '\u03C6', -# # U+041A (Cyrillic Capital Letter Ka): cp1251 -# '\u041A', -# # U+05D0 (Hebrew Letter Alef): Encodable to cp424 -# '\u05D0', -# # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic -# '\u060C', -# # U+062A (Arabic Letter Teh): cp720 -# '\u062A', -# # U+0E01 (Thai Character Ko Kai): cp874 -# '\u0E01', -# -# # Then try more "special" characters. "special" because they may be -# # interpreted or displayed differently depending on the exact locale -# # encoding and the font. -# -# # U+00A0 (No-Break Space) -# '\u00A0', -# # U+20AC (Euro Sign) -# '\u20AC', -# ): -# try: -# os.fsdecode(os.fsencode(character)) -# except UnicodeError: -# pass -# else: -# FS_NONASCII = character -# break -# -# # TESTFN_UNICODE is a non-ascii filename -# TESTFN_UNICODE = TESTFN + "-\xe0\xf2\u0258\u0141\u011f" -# if sys.platform == 'darwin': -# # In Mac OS X's VFS API file names are, by definition, canonically -# # decomposed Unicode, encoded using UTF-8. See QA1173: -# # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html -# import unicodedata -# TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE) -# TESTFN_ENCODING = sys.getfilesystemencoding() -# -# # TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be -# # encoded by the filesystem encoding (in strict mode). It can be None if we -# # cannot generate such filename. -# TESTFN_UNENCODABLE = None -# if os.name in ('nt', 'ce'): -# # skip win32s (0) or Windows 9x/ME (1) -# if sys.getwindowsversion().platform >= 2: -# # Different kinds of characters from various languages to minimize the -# # probability that the whole name is encodable to MBCS (issue #9819) -# TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80" -# try: -# TESTFN_UNENCODABLE.encode(TESTFN_ENCODING) -# except UnicodeEncodeError: -# pass -# else: -# print('WARNING: The filename %r CAN be encoded by the filesystem encoding (%s). ' -# 'Unicode filename tests may not be effective' -# % (TESTFN_UNENCODABLE, TESTFN_ENCODING)) -# TESTFN_UNENCODABLE = None -# # Mac OS X denies unencodable filenames (invalid utf-8) -# elif sys.platform != 'darwin': -# try: -# # ascii and utf-8 cannot encode the byte 0xff -# b'\xff'.decode(TESTFN_ENCODING) -# except UnicodeDecodeError: -# # 0xff will be encoded using the surrogate character u+DCFF -# TESTFN_UNENCODABLE = TESTFN \ -# + b'-\xff'.decode(TESTFN_ENCODING, 'surrogateescape') -# else: -# # File system encoding (eg. ISO-8859-* encodings) can encode -# # the byte 0xff. Skip some unicode filename tests. -# pass -# -# # TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be -# # decoded from the filesystem encoding (in strict mode). It can be None if we -# # cannot generate such filename (ex: the latin1 encoding can decode any byte -# # sequence). On UNIX, TESTFN_UNDECODABLE can be decoded by os.fsdecode() thanks -# # to the surrogateescape error handler (PEP 383), but not from the filesystem -# # encoding in strict mode. -# TESTFN_UNDECODABLE = None -# for name in ( -# # b'\xff' is not decodable by os.fsdecode() with code page 932. Windows -# # accepts it to create a file or a directory, or don't accept to enter to -# # such directory (when the bytes name is used). So test b'\xe7' first: it is -# # not decodable from cp932. -# b'\xe7w\xf0', -# # undecodable from ASCII, UTF-8 -# b'\xff', -# # undecodable from iso8859-3, iso8859-6, iso8859-7, cp424, iso8859-8, cp856 -# # and cp857 -# b'\xae\xd5' -# # undecodable from UTF-8 (UNIX and Mac OS X) -# b'\xed\xb2\x80', b'\xed\xb4\x80', -# # undecodable from shift_jis, cp869, cp874, cp932, cp1250, cp1251, cp1252, -# # cp1253, cp1254, cp1255, cp1257, cp1258 -# b'\x81\x98', -# ): -# try: -# name.decode(TESTFN_ENCODING) -# except UnicodeDecodeError: -# TESTFN_UNDECODABLE = os.fsencode(TESTFN) + name -# break -# -# if FS_NONASCII: -# TESTFN_NONASCII = TESTFN + '-' + FS_NONASCII -# else: -# TESTFN_NONASCII = None - -# Save the initial cwd -SAVEDCWD = os.getcwd() - -@contextlib.contextmanager -def temp_cwd(name='tempcwd', quiet=False, path=None): - """ - Context manager that temporarily changes the CWD. - - An existing path may be provided as *path*, in which case this - function makes no changes to the file system. - - Otherwise, the new CWD is created in the current directory and it's - named *name*. If *quiet* is False (default) and it's not possible to - create or change the CWD, an error is raised. If it's True, only a - warning is raised and the original CWD is used. - """ - saved_dir = os.getcwd() - is_temporary = False - if path is None: - path = name - try: - os.mkdir(name) - is_temporary = True - except OSError: - if not quiet: - raise - warnings.warn('tests may fail, unable to create temp CWD ' + name, - RuntimeWarning, stacklevel=3) - try: - os.chdir(path) - except OSError: - if not quiet: - raise - warnings.warn('tests may fail, unable to change the CWD to ' + path, - RuntimeWarning, stacklevel=3) - try: - yield os.getcwd() - finally: - os.chdir(saved_dir) - if is_temporary: - rmtree(name) - - -if hasattr(os, "umask"): - @contextlib.contextmanager - def temp_umask(umask): - """Context manager that temporarily sets the process umask.""" - oldmask = os.umask(umask) - try: - yield - finally: - os.umask(oldmask) - - -def findfile(file, here=__file__, subdir=None): - """Try to find a file on sys.path and the working directory. If it is not - found the argument passed to the function is returned (this does not - necessarily signal failure; could still be the legitimate path).""" - if os.path.isabs(file): - return file - if subdir is not None: - file = os.path.join(subdir, file) - path = sys.path - path = [os.path.dirname(here)] + path - for dn in path: - fn = os.path.join(dn, file) - if os.path.exists(fn): return fn - return file - -def create_empty_file(filename): - """Create an empty file. If the file already exists, truncate it.""" - fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) - os.close(fd) - -def sortdict(dict): - "Like repr(dict), but in sorted order." - items = sorted(dict.items()) - reprpairs = ["%r: %r" % pair for pair in items] - withcommas = ", ".join(reprpairs) - return "{%s}" % withcommas - -def make_bad_fd(): - """ - Create an invalid file descriptor by opening and closing a file and return - its fd. - """ - file = open(TESTFN, "wb") - try: - return file.fileno() - finally: - file.close() - unlink(TESTFN) - -def check_syntax_error(testcase, statement): - testcase.assertRaises(SyntaxError, compile, statement, - '', 'exec') - -def open_urlresource(url, *args, **kw): - from future.backports.urllib import (request as urllib_request, - parse as urllib_parse) - - check = kw.pop('check', None) - - filename = urllib_parse.urlparse(url)[2].split('/')[-1] # '/': it's URL! - - fn = os.path.join(os.path.dirname(__file__), "data", filename) - - def check_valid_file(fn): - f = open(fn, *args, **kw) - if check is None: - return f - elif check(f): - f.seek(0) - return f - f.close() - - if os.path.exists(fn): - f = check_valid_file(fn) - if f is not None: - return f - unlink(fn) - - # Verify the requirement before downloading the file - requires('urlfetch') - - print('\tfetching %s ...' % url, file=get_original_stdout()) - f = urllib_request.urlopen(url, timeout=15) - try: - with open(fn, "wb") as out: - s = f.read() - while s: - out.write(s) - s = f.read() - finally: - f.close() - - f = check_valid_file(fn) - if f is not None: - return f - raise TestFailed('invalid resource %r' % fn) - - -class WarningsRecorder(object): - """Convenience wrapper for the warnings list returned on - entry to the warnings.catch_warnings() context manager. - """ - def __init__(self, warnings_list): - self._warnings = warnings_list - self._last = 0 - - def __getattr__(self, attr): - if len(self._warnings) > self._last: - return getattr(self._warnings[-1], attr) - elif attr in warnings.WarningMessage._WARNING_DETAILS: - return None - raise AttributeError("%r has no attribute %r" % (self, attr)) - - @property - def warnings(self): - return self._warnings[self._last:] - - def reset(self): - self._last = len(self._warnings) - - -def _filterwarnings(filters, quiet=False): - """Catch the warnings, then check if all the expected - warnings have been raised and re-raise unexpected warnings. - If 'quiet' is True, only re-raise the unexpected warnings. - """ - # Clear the warning registry of the calling module - # in order to re-raise the warnings. - frame = sys._getframe(2) - registry = frame.f_globals.get('__warningregistry__') - if registry: - if utils.PY3: - registry.clear() - else: - # Py2-compatible: - for i in range(len(registry)): - registry.pop() - with warnings.catch_warnings(record=True) as w: - # Set filter "always" to record all warnings. Because - # test_warnings swap the module, we need to look up in - # the sys.modules dictionary. - sys.modules['warnings'].simplefilter("always") - yield WarningsRecorder(w) - # Filter the recorded warnings - reraise = list(w) - missing = [] - for msg, cat in filters: - seen = False - for w in reraise[:]: - warning = w.message - # Filter out the matching messages - if (re.match(msg, str(warning), re.I) and - issubclass(warning.__class__, cat)): - seen = True - reraise.remove(w) - if not seen and not quiet: - # This filter caught nothing - missing.append((msg, cat.__name__)) - if reraise: - raise AssertionError("unhandled warning %s" % reraise[0]) - if missing: - raise AssertionError("filter (%r, %s) did not catch any warning" % - missing[0]) - - -@contextlib.contextmanager -def check_warnings(*filters, **kwargs): - """Context manager to silence warnings. - - Accept 2-tuples as positional arguments: - ("message regexp", WarningCategory) - - Optional argument: - - if 'quiet' is True, it does not fail if a filter catches nothing - (default True without argument, - default False if some filters are defined) - - Without argument, it defaults to: - check_warnings(("", Warning), quiet=True) - """ - quiet = kwargs.get('quiet') - if not filters: - filters = (("", Warning),) - # Preserve backward compatibility - if quiet is None: - quiet = True - return _filterwarnings(filters, quiet) - - -class CleanImport(object): - """Context manager to force import to return a new module reference. - - This is useful for testing module-level behaviours, such as - the emission of a DeprecationWarning on import. - - Use like this: - - with CleanImport("foo"): - importlib.import_module("foo") # new reference - """ - - def __init__(self, *module_names): - self.original_modules = sys.modules.copy() - for module_name in module_names: - if module_name in sys.modules: - module = sys.modules[module_name] - # It is possible that module_name is just an alias for - # another module (e.g. stub for modules renamed in 3.x). - # In that case, we also need delete the real module to clear - # the import cache. - if module.__name__ != module_name: - del sys.modules[module.__name__] - del sys.modules[module_name] - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - sys.modules.update(self.original_modules) - -### Added for python-future: -if utils.PY3: - import collections.abc - mybase = collections.abc.MutableMapping -else: - import UserDict - mybase = UserDict.DictMixin -### - -class EnvironmentVarGuard(mybase): - - """Class to help protect the environment variable properly. Can be used as - a context manager.""" - - def __init__(self): - self._environ = os.environ - self._changed = {} - - def __getitem__(self, envvar): - return self._environ[envvar] - - def __setitem__(self, envvar, value): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - self._environ[envvar] = value - - def __delitem__(self, envvar): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - if envvar in self._environ: - del self._environ[envvar] - - def keys(self): - return self._environ.keys() - - def __iter__(self): - return iter(self._environ) - - def __len__(self): - return len(self._environ) - - def set(self, envvar, value): - self[envvar] = value - - def unset(self, envvar): - del self[envvar] - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - for (k, v) in self._changed.items(): - if v is None: - if k in self._environ: - del self._environ[k] - else: - self._environ[k] = v - os.environ = self._environ - - -class DirsOnSysPath(object): - """Context manager to temporarily add directories to sys.path. - - This makes a copy of sys.path, appends any directories given - as positional arguments, then reverts sys.path to the copied - settings when the context ends. - - Note that *all* sys.path modifications in the body of the - context manager, including replacement of the object, - will be reverted at the end of the block. - """ - - def __init__(self, *paths): - self.original_value = sys.path[:] - self.original_object = sys.path - sys.path.extend(paths) - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - sys.path = self.original_object - sys.path[:] = self.original_value - - -class TransientResource(object): - - """Raise ResourceDenied if an exception is raised while the context manager - is in effect that matches the specified exception and attributes.""" - - def __init__(self, exc, **kwargs): - self.exc = exc - self.attrs = kwargs - - def __enter__(self): - return self - - def __exit__(self, type_=None, value=None, traceback=None): - """If type_ is a subclass of self.exc and value has attributes matching - self.attrs, raise ResourceDenied. Otherwise let the exception - propagate (if any).""" - if type_ is not None and issubclass(self.exc, type_): - for attr, attr_value in self.attrs.items(): - if not hasattr(value, attr): - break - if getattr(value, attr) != attr_value: - break - else: - raise ResourceDenied("an optional resource is not available") - -# Context managers that raise ResourceDenied when various issues -# with the Internet connection manifest themselves as exceptions. -# XXX deprecate these and use transient_internet() instead -time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) -socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) -ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) - - -@contextlib.contextmanager -def transient_internet(resource_name, timeout=30.0, errnos=()): - """Return a context manager that raises ResourceDenied when various issues - with the Internet connection manifest themselves as exceptions.""" - default_errnos = [ - ('ECONNREFUSED', 111), - ('ECONNRESET', 104), - ('EHOSTUNREACH', 113), - ('ENETUNREACH', 101), - ('ETIMEDOUT', 110), - ] - default_gai_errnos = [ - ('EAI_AGAIN', -3), - ('EAI_FAIL', -4), - ('EAI_NONAME', -2), - ('EAI_NODATA', -5), - # Encountered when trying to resolve IPv6-only hostnames - ('WSANO_DATA', 11004), - ] - - denied = ResourceDenied("Resource %r is not available" % resource_name) - captured_errnos = errnos - gai_errnos = [] - if not captured_errnos: - captured_errnos = [getattr(errno, name, num) - for (name, num) in default_errnos] - gai_errnos = [getattr(socket, name, num) - for (name, num) in default_gai_errnos] - - def filter_error(err): - n = getattr(err, 'errno', None) - if (isinstance(err, socket.timeout) or - (isinstance(err, socket.gaierror) and n in gai_errnos) or - n in captured_errnos): - if not verbose: - sys.stderr.write(denied.args[0] + "\n") - # Was: raise denied from err - # For Python-Future: - exc = denied - exc.__cause__ = err - raise exc - - old_timeout = socket.getdefaulttimeout() - try: - if timeout is not None: - socket.setdefaulttimeout(timeout) - yield - except IOError as err: - # urllib can wrap original socket errors multiple times (!), we must - # unwrap to get at the original error. - while True: - a = err.args - if len(a) >= 1 and isinstance(a[0], IOError): - err = a[0] - # The error can also be wrapped as args[1]: - # except socket.error as msg: - # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) - elif len(a) >= 2 and isinstance(a[1], IOError): - err = a[1] - else: - break - filter_error(err) - raise - # XXX should we catch generic exceptions and look for their - # __cause__ or __context__? - finally: - socket.setdefaulttimeout(old_timeout) - - -@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.""" - import io - orig_stdout = getattr(sys, stream_name) - setattr(sys, stream_name, io.StringIO()) - 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 s: - print("hello") - self.assertEqual(s.getvalue(), "hello") - """ - return captured_output("stdout") - -def captured_stderr(): - return captured_output("stderr") - -def captured_stdin(): - return captured_output("stdin") - - -def gc_collect(): - """Force as many objects as possible to be collected. - - In non-CPython implementations of Python, this is needed because timely - deallocation is not guaranteed by the garbage collector. (Even in CPython - this can be the case in case of reference cycles.) This means that __del__ - methods may be called later than expected and weakrefs may remain alive for - longer than expected. This function tries its best to force all garbage - objects to disappear. - """ - gc.collect() - if is_jython: - time.sleep(0.1) - gc.collect() - gc.collect() - -@contextlib.contextmanager -def disable_gc(): - have_gc = gc.isenabled() - gc.disable() - try: - yield - finally: - if have_gc: - gc.enable() - - -def python_is_optimized(): - """Find if Python was built with optimizations.""" - # We don't have sysconfig on Py2.6: - import sysconfig - cflags = sysconfig.get_config_var('PY_CFLAGS') or '' - final_opt = "" - for opt in cflags.split(): - if opt.startswith('-O'): - final_opt = opt - return final_opt != '' and final_opt != '-O0' - - -_header = 'nP' -_align = '0n' -if hasattr(sys, "gettotalrefcount"): - _header = '2P' + _header - _align = '0P' -_vheader = _header + 'n' - -def calcobjsize(fmt): - return struct.calcsize(_header + fmt + _align) - -def calcvobjsize(fmt): - return struct.calcsize(_vheader + fmt + _align) - - -_TPFLAGS_HAVE_GC = 1<<14 -_TPFLAGS_HEAPTYPE = 1<<9 - -def check_sizeof(test, o, size): - result = sys.getsizeof(o) - # add GC header size - if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ - ((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))): - size += _testcapi.SIZEOF_PYGC_HEAD - msg = 'wrong size for %s: got %d, expected %d' \ - % (type(o), result, size) - test.assertEqual(result, size, msg) - -#======================================================================= -# Decorator for running a function in a different locale, correctly resetting -# it afterwards. - -def run_with_locale(catstr, *locales): - def decorator(func): - def inner(*args, **kwds): - try: - import locale - category = getattr(locale, catstr) - orig_locale = locale.setlocale(category) - except AttributeError: - # if the test author gives us an invalid category string - raise - except: - # cannot retrieve original locale, so do nothing - locale = orig_locale = None - else: - for loc in locales: - try: - locale.setlocale(category, loc) - break - except: - pass - - # now run the function, resetting the locale on exceptions - try: - return func(*args, **kwds) - finally: - if locale and orig_locale: - locale.setlocale(category, orig_locale) - inner.__name__ = func.__name__ - inner.__doc__ = func.__doc__ - return inner - return decorator - -#======================================================================= -# Decorator for running a function in a specific timezone, correctly -# resetting it afterwards. - -def run_with_tz(tz): - def decorator(func): - def inner(*args, **kwds): - try: - tzset = time.tzset - except AttributeError: - raise unittest.SkipTest("tzset required") - if 'TZ' in os.environ: - orig_tz = os.environ['TZ'] - else: - orig_tz = None - os.environ['TZ'] = tz - tzset() - - # now run the function, resetting the tz on exceptions - try: - return func(*args, **kwds) - finally: - if orig_tz is None: - del os.environ['TZ'] - else: - os.environ['TZ'] = orig_tz - time.tzset() - - inner.__name__ = func.__name__ - inner.__doc__ = func.__doc__ - return inner - return decorator - -#======================================================================= -# Big-memory-test support. Separate from 'resources' because memory use -# should be configurable. - -# Some handy shorthands. Note that these are used for byte-limits as well -# as size-limits, in the various bigmem tests -_1M = 1024*1024 -_1G = 1024 * _1M -_2G = 2 * _1G -_4G = 4 * _1G - -MAX_Py_ssize_t = sys.maxsize - -def set_memlimit(limit): - global max_memuse - global real_max_memuse - sizes = { - 'k': 1024, - 'm': _1M, - 'g': _1G, - 't': 1024*_1G, - } - m = re.match(r'(\d+(\.\d+)?) (K|M|G|T)b?$', limit, - re.IGNORECASE | re.VERBOSE) - if m is None: - raise ValueError('Invalid memory limit %r' % (limit,)) - memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()]) - real_max_memuse = memlimit - if memlimit > MAX_Py_ssize_t: - memlimit = MAX_Py_ssize_t - if memlimit < _2G - 1: - raise ValueError('Memory limit %r too low to be useful' % (limit,)) - max_memuse = memlimit - -class _MemoryWatchdog(object): - """An object which periodically watches the process' memory consumption - and prints it out. - """ - - def __init__(self): - self.procfile = '/proc/{pid}/statm'.format(pid=os.getpid()) - self.started = False - - def start(self): - try: - f = open(self.procfile, 'r') - except OSError as e: - warnings.warn('/proc not available for stats: {0}'.format(e), - RuntimeWarning) - sys.stderr.flush() - return - - watchdog_script = findfile("memory_watchdog.py") - self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script], - stdin=f, stderr=subprocess.DEVNULL) - f.close() - self.started = True - - def stop(self): - if self.started: - self.mem_watchdog.terminate() - self.mem_watchdog.wait() - - -def bigmemtest(size, memuse, dry_run=True): - """Decorator for bigmem tests. - - 'minsize' is the minimum useful size for the test (in arbitrary, - test-interpreted units.) 'memuse' is the number of 'bytes per size' for - the test, or a good estimate of it. - - if 'dry_run' is False, it means the test doesn't support dummy runs - when -M is not specified. - """ - def decorator(f): - def wrapper(self): - size = wrapper.size - memuse = wrapper.memuse - if not real_max_memuse: - maxsize = 5147 - else: - maxsize = size - - if ((real_max_memuse or not dry_run) - and real_max_memuse < maxsize * memuse): - raise unittest.SkipTest( - "not enough memory: %.1fG minimum needed" - % (size * memuse / (1024 ** 3))) - - if real_max_memuse and verbose: - print() - print(" ... expected peak memory use: {peak:.1f}G" - .format(peak=size * memuse / (1024 ** 3))) - watchdog = _MemoryWatchdog() - watchdog.start() - else: - watchdog = None - - try: - return f(self, maxsize) - finally: - if watchdog: - watchdog.stop() - - wrapper.size = size - wrapper.memuse = memuse - return wrapper - return decorator - -def bigaddrspacetest(f): - """Decorator for tests that fill the address space.""" - def wrapper(self): - if max_memuse < MAX_Py_ssize_t: - if MAX_Py_ssize_t >= 2**63 - 1 and max_memuse >= 2**31: - raise unittest.SkipTest( - "not enough memory: try a 32-bit build instead") - else: - raise unittest.SkipTest( - "not enough memory: %.1fG minimum needed" - % (MAX_Py_ssize_t / (1024 ** 3))) - else: - return f(self) - return wrapper - -#======================================================================= -# unittest integration. - -class BasicTestRunner(object): - def run(self, test): - result = unittest.TestResult() - test(result) - return result - -def _id(obj): - return obj - -def requires_resource(resource): - if resource == 'gui' and not _is_gui_available(): - return unittest.skip("resource 'gui' is not available") - if is_resource_enabled(resource): - return _id - else: - return unittest.skip("resource {0!r} is not enabled".format(resource)) - -def cpython_only(test): - """ - Decorator for tests only applicable on CPython. - """ - return impl_detail(cpython=True)(test) - -def impl_detail(msg=None, **guards): - if check_impl_detail(**guards): - return _id - if msg is None: - guardnames, default = _parse_guards(guards) - if default: - msg = "implementation detail not available on {0}" - else: - msg = "implementation detail specific to {0}" - guardnames = sorted(guardnames.keys()) - msg = msg.format(' or '.join(guardnames)) - return unittest.skip(msg) - -def _parse_guards(guards): - # Returns a tuple ({platform_name: run_me}, default_value) - if not guards: - return ({'cpython': True}, False) - is_true = list(guards.values())[0] - assert list(guards.values()) == [is_true] * len(guards) # all True or all False - return (guards, not is_true) - -# Use the following check to guard CPython's implementation-specific tests -- -# or to run them only on the implementation(s) guarded by the arguments. -def check_impl_detail(**guards): - """This function returns True or False depending on the host platform. - Examples: - if check_impl_detail(): # only on CPython (default) - if check_impl_detail(jython=True): # only on Jython - if check_impl_detail(cpython=False): # everywhere except on CPython - """ - guards, default = _parse_guards(guards) - return guards.get(platform.python_implementation().lower(), default) - - -def no_tracing(func): - """Decorator to temporarily turn off tracing for the duration of a test.""" - if not hasattr(sys, 'gettrace'): - return func - else: - @functools.wraps(func) - def wrapper(*args, **kwargs): - original_trace = sys.gettrace() - try: - sys.settrace(None) - return func(*args, **kwargs) - finally: - sys.settrace(original_trace) - return wrapper - - -def refcount_test(test): - """Decorator for tests which involve reference counting. - - To start, the decorator does not run the test if is not run by CPython. - After that, any trace function is unset during the test to prevent - unexpected refcounts caused by the trace function. - - """ - return no_tracing(cpython_only(test)) - - -def _filter_suite(suite, pred): - """Recursively filter test cases in a suite based on a predicate.""" - newtests = [] - for test in suite._tests: - if isinstance(test, unittest.TestSuite): - _filter_suite(test, pred) - newtests.append(test) - else: - if pred(test): - newtests.append(test) - suite._tests = newtests - -def _run_suite(suite): - """Run tests from a unittest.TestSuite-derived class.""" - if verbose: - runner = unittest.TextTestRunner(sys.stdout, verbosity=2, - failfast=failfast) - else: - runner = BasicTestRunner() - - result = runner.run(suite) - if not result.wasSuccessful(): - if len(result.errors) == 1 and not result.failures: - err = result.errors[0][1] - elif len(result.failures) == 1 and not result.errors: - err = result.failures[0][1] - else: - err = "multiple errors occurred" - if not verbose: err += "; run in verbose mode for details" - raise TestFailed(err) - - -def run_unittest(*classes): - """Run tests from unittest.TestCase-derived classes.""" - valid_types = (unittest.TestSuite, unittest.TestCase) - suite = unittest.TestSuite() - for cls in classes: - if isinstance(cls, str): - if cls in sys.modules: - suite.addTest(unittest.findTestCases(sys.modules[cls])) - else: - raise ValueError("str arguments must be keys in sys.modules") - elif isinstance(cls, valid_types): - suite.addTest(cls) - else: - suite.addTest(unittest.makeSuite(cls)) - def case_pred(test): - if match_tests is None: - return True - for name in test.id().split("."): - if fnmatch.fnmatchcase(name, match_tests): - return True - return False - _filter_suite(suite, case_pred) - _run_suite(suite) - -# We don't have sysconfig on Py2.6: -# #======================================================================= -# # Check for the presence of docstrings. -# -# HAVE_DOCSTRINGS = (check_impl_detail(cpython=False) or -# sys.platform == 'win32' or -# sysconfig.get_config_var('WITH_DOC_STRINGS')) -# -# requires_docstrings = unittest.skipUnless(HAVE_DOCSTRINGS, -# "test requires docstrings") -# -# -# #======================================================================= -# doctest driver. - -def run_doctest(module, verbosity=None, optionflags=0): - """Run doctest on the given module. Return (#failures, #tests). - - If optional argument verbosity is not specified (or is None), pass - support's belief about verbosity on to doctest. Else doctest's - usual behavior is used (it searches sys.argv for -v). - """ - - import doctest - - if verbosity is None: - verbosity = verbose - else: - verbosity = None - - f, t = doctest.testmod(module, verbose=verbosity, optionflags=optionflags) - if f: - raise TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print('doctest (%s) ... %d tests with zero failures' % - (module.__name__, t)) - return f, t - - -#======================================================================= -# Support for saving and restoring the imported modules. - -def modules_setup(): - return sys.modules.copy(), - -def modules_cleanup(oldmodules): - # Encoders/decoders are registered permanently within the internal - # codec cache. If we destroy the corresponding modules their - # globals will be set to None which will trip up the cached functions. - encodings = [(k, v) for k, v in sys.modules.items() - if k.startswith('encodings.')] - # Was: - # sys.modules.clear() - # Py2-compatible: - for i in range(len(sys.modules)): - sys.modules.pop() - - sys.modules.update(encodings) - # XXX: This kind of problem can affect more than just encodings. In particular - # extension modules (such as _ssl) don't cope with reloading properly. - # Really, test modules should be cleaning out the test specific modules they - # know they added (ala test_runpy) rather than relying on this function (as - # test_importhooks and test_pkg do currently). - # Implicitly imported *real* modules should be left alone (see issue 10556). - sys.modules.update(oldmodules) - -#======================================================================= -# Backported versions of threading_setup() and threading_cleanup() which don't refer -# to threading._dangling (not available on Py2.7). - -# Threading support to prevent reporting refleaks when running regrtest.py -R - -# NOTE: we use thread._count() rather than threading.enumerate() (or the -# moral equivalent thereof) because a threading.Thread object is still alive -# until its __bootstrap() method has returned, even after it has been -# unregistered from the threading module. -# thread._count(), on the other hand, only gets decremented *after* the -# __bootstrap() method has returned, which gives us reliable reference counts -# at the end of a test run. - -def threading_setup(): - if _thread: - return _thread._count(), - else: - return 1, - -def threading_cleanup(nb_threads): - if not _thread: - return - - _MAX_COUNT = 10 - for count in range(_MAX_COUNT): - n = _thread._count() - if n == nb_threads: - break - time.sleep(0.1) - # XXX print a warning in case of failure? - -def reap_threads(func): - """Use this function when threads are being used. This will - ensure that the threads are cleaned up even when the test fails. - If threading is unavailable this function does nothing. - """ - if not _thread: - return func - - @functools.wraps(func) - def decorator(*args): - key = threading_setup() - try: - return func(*args) - finally: - threading_cleanup(*key) - return decorator - -def reap_children(): - """Use this function at the end of test_main() whenever sub-processes - are started. This will help ensure that no extra children (zombies) - stick around to hog resources and create problems when looking - for refleaks. - """ - - # Reap all our dead child processes so we don't leave zombies around. - # These hog resources and might be causing some of the buildbots to die. - if hasattr(os, 'waitpid'): - any_process = -1 - while True: - try: - # This will raise an exception on Windows. That's ok. - pid, status = os.waitpid(any_process, os.WNOHANG) - if pid == 0: - break - except: - break - -@contextlib.contextmanager -def swap_attr(obj, attr, new_val): - """Temporary swap out an attribute with a new object. - - Usage: - with swap_attr(obj, "attr", 5): - ... - - This will set obj.attr to 5 for the duration of the with: block, - restoring the old value at the end of the block. If `attr` doesn't - exist on `obj`, it will be created and then deleted at the end of the - block. - """ - if hasattr(obj, attr): - real_val = getattr(obj, attr) - setattr(obj, attr, new_val) - try: - yield - finally: - setattr(obj, attr, real_val) - else: - setattr(obj, attr, new_val) - try: - yield - finally: - delattr(obj, attr) - -@contextlib.contextmanager -def swap_item(obj, item, new_val): - """Temporary swap out an item with a new object. - - Usage: - with swap_item(obj, "item", 5): - ... - - This will set obj["item"] to 5 for the duration of the with: block, - restoring the old value at the end of the block. If `item` doesn't - exist on `obj`, it will be created and then deleted at the end of the - block. - """ - if item in obj: - real_val = obj[item] - obj[item] = new_val - try: - yield - finally: - obj[item] = real_val - else: - obj[item] = new_val - try: - yield - finally: - del obj[item] - -def strip_python_stderr(stderr): - """Strip the stderr of a Python process from potential debug output - emitted by the interpreter. - - This will typically be run on the result of the communicate() method - of a subprocess.Popen object. - """ - stderr = re.sub(br"\[\d+ refs\]\r?\n?", b"", stderr).strip() - return stderr - -def args_from_interpreter_flags(): - """Return a list of command-line arguments reproducing the current - settings in sys.flags and sys.warnoptions.""" - return subprocess._args_from_interpreter_flags() - -#============================================================ -# Support for assertions about logging. -#============================================================ - -class TestHandler(logging.handlers.BufferingHandler): - def __init__(self, matcher): - # BufferingHandler takes a "capacity" argument - # so as to know when to flush. As we're overriding - # shouldFlush anyway, we can set a capacity of zero. - # You can call flush() manually to clear out the - # buffer. - logging.handlers.BufferingHandler.__init__(self, 0) - self.matcher = matcher - - def shouldFlush(self): - return False - - def emit(self, record): - self.format(record) - self.buffer.append(record.__dict__) - - def matches(self, **kwargs): - """ - Look for a saved dict whose keys/values match the supplied arguments. - """ - result = False - for d in self.buffer: - if self.matcher.matches(d, **kwargs): - result = True - break - return result - -class Matcher(object): - - _partial_matches = ('msg', 'message') - - def matches(self, d, **kwargs): - """ - Try to match a single dict with the supplied arguments. - - Keys whose values are strings and which are in self._partial_matches - will be checked for partial (i.e. substring) matches. You can extend - this scheme to (for example) do regular expression matching, etc. - """ - result = True - for k in kwargs: - v = kwargs[k] - dv = d.get(k) - if not self.match_value(k, dv, v): - result = False - break - return result - - def match_value(self, k, dv, v): - """ - Try to match a single stored value (dv) with a supplied value (v). - """ - if type(v) != type(dv): - result = False - elif type(dv) is not str or k not in self._partial_matches: - result = (v == dv) - else: - result = dv.find(v) >= 0 - return result - - -_can_symlink = None -def can_symlink(): - global _can_symlink - if _can_symlink is not None: - return _can_symlink - symlink_path = TESTFN + "can_symlink" - try: - os.symlink(TESTFN, symlink_path) - can = True - except (OSError, NotImplementedError, AttributeError): - can = False - else: - os.remove(symlink_path) - _can_symlink = can - return can - -def skip_unless_symlink(test): - """Skip decorator for tests that require functional symlink""" - ok = can_symlink() - msg = "Requires functional symlink implementation" - return test if ok else unittest.skip(msg)(test) - -_can_xattr = None -def can_xattr(): - global _can_xattr - if _can_xattr is not None: - return _can_xattr - if not hasattr(os, "setxattr"): - can = False - else: - tmp_fp, tmp_name = tempfile.mkstemp() - try: - with open(TESTFN, "wb") as fp: - try: - # TESTFN & tempfile may use different file systems with - # different capabilities - os.setxattr(tmp_fp, b"user.test", b"") - os.setxattr(fp.fileno(), b"user.test", b"") - # Kernels < 2.6.39 don't respect setxattr flags. - kernel_version = platform.release() - m = re.match("2.6.(\d{1,2})", kernel_version) - can = m is None or int(m.group(1)) >= 39 - except OSError: - can = False - finally: - unlink(TESTFN) - unlink(tmp_name) - _can_xattr = can - return can - -def skip_unless_xattr(test): - """Skip decorator for tests that require functional extended attributes""" - ok = can_xattr() - msg = "no non-broken extended attribute support" - return test if ok else unittest.skip(msg)(test) - - -if sys.platform.startswith('win'): - @contextlib.contextmanager - def suppress_crash_popup(): - """Disable Windows Error Reporting dialogs using SetErrorMode.""" - # see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx - # GetErrorMode is not available on Windows XP and Windows Server 2003, - # but SetErrorMode returns the previous value, so we can use that - import ctypes - k32 = ctypes.windll.kernel32 - SEM_NOGPFAULTERRORBOX = 0x02 - old_error_mode = k32.SetErrorMode(SEM_NOGPFAULTERRORBOX) - k32.SetErrorMode(old_error_mode | SEM_NOGPFAULTERRORBOX) - try: - yield - finally: - k32.SetErrorMode(old_error_mode) -else: - # this is a no-op for other platforms - @contextlib.contextmanager - def suppress_crash_popup(): - yield - - -def patch(test_instance, object_to_patch, attr_name, new_value): - """Override 'object_to_patch'.'attr_name' with 'new_value'. - - Also, add a cleanup procedure to 'test_instance' to restore - 'object_to_patch' value for 'attr_name'. - The 'attr_name' should be a valid attribute for 'object_to_patch'. - - """ - # check that 'attr_name' is a real attribute for 'object_to_patch' - # will raise AttributeError if it does not exist - getattr(object_to_patch, attr_name) - - # keep a copy of the old value - attr_is_local = False - try: - old_value = object_to_patch.__dict__[attr_name] - except (AttributeError, KeyError): - old_value = getattr(object_to_patch, attr_name, None) - else: - attr_is_local = True - - # restore the value when the test is done - def cleanup(): - if attr_is_local: - setattr(object_to_patch, attr_name, old_value) - else: - delattr(object_to_patch, attr_name) - - test_instance.addCleanup(cleanup) - - # actually override the attribute - setattr(object_to_patch, attr_name, new_value) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/total_ordering.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/total_ordering.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/total_ordering.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/total_ordering.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -""" -For Python < 2.7.2. total_ordering in versions prior to 2.7.2 is buggy. -See http://bugs.python.org/issue10042 for details. For these versions use -code borrowed from Python 2.7.3. - -From django.utils. -""" - -import sys -if sys.version_info >= (2, 7, 2): - from functools import total_ordering -else: - def total_ordering(cls): - """Class decorator that fills in missing ordering methods""" - convert = { - '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), - ('__le__', lambda self, other: self < other or self == other), - ('__ge__', lambda self, other: not self < other)], - '__le__': [('__ge__', lambda self, other: not self <= other or self == other), - ('__lt__', lambda self, other: self <= other and not self == other), - ('__gt__', lambda self, other: not self <= other)], - '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), - ('__ge__', lambda self, other: self > other or self == other), - ('__le__', lambda self, other: not self > other)], - '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), - ('__gt__', lambda self, other: self >= other and not self == other), - ('__lt__', lambda self, other: not self >= other)] - } - roots = set(dir(cls)) & set(convert) - if not roots: - raise ValueError('must define at least one ordering operation: < > <= >=') - root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ - for opname, opfunc in convert[root]: - if opname not in roots: - opfunc.__name__ = opname - opfunc.__doc__ = getattr(int, opname).__doc__ - setattr(cls, opname, opfunc) - return cls diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/error.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/error.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/error.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/error.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -"""Exception classes raised by urllib. - -The base exception class is URLError, which inherits from IOError. It -doesn't define any behavior of its own, but is the base class for all -exceptions defined in this package. - -HTTPError is an exception class that is also a valid HTTP response -instance. It behaves this way because HTTP protocol errors are valid -responses, with a status code, headers, and a body. In some contexts, -an application may want to handle an exception like a regular -response. -""" -from __future__ import absolute_import, division, unicode_literals -from future import standard_library - -from future.backports.urllib import response as urllib_response - - -__all__ = ['URLError', 'HTTPError', 'ContentTooShortError'] - - -# do these error classes make sense? -# make sure all of the IOError stuff is overridden. we just want to be -# subtypes. - -class URLError(IOError): - # URLError is a sub-type of IOError, but it doesn't share any of - # the implementation. need to override __init__ and __str__. - # It sets self.args for compatibility with other EnvironmentError - # subclasses, but args doesn't have the typical format with errno in - # slot 0 and strerror in slot 1. This may be better than nothing. - def __init__(self, reason, filename=None): - self.args = reason, - self.reason = reason - if filename is not None: - self.filename = filename - - def __str__(self): - return '' % self.reason - -class HTTPError(URLError, urllib_response.addinfourl): - """Raised when HTTP error occurs, but also acts like non-error return""" - __super_init = urllib_response.addinfourl.__init__ - - def __init__(self, url, code, msg, hdrs, fp): - self.code = code - self.msg = msg - self.hdrs = hdrs - self.fp = fp - self.filename = url - # The addinfourl classes depend on fp being a valid file - # object. In some cases, the HTTPError may not have a valid - # file object. If this happens, the simplest workaround is to - # not initialize the base classes. - if fp is not None: - self.__super_init(fp, hdrs, url, code) - - def __str__(self): - return 'HTTP Error %s: %s' % (self.code, self.msg) - - # since URLError specifies a .reason attribute, HTTPError should also - # provide this attribute. See issue13211 for discussion. - @property - def reason(self): - return self.msg - - def info(self): - return self.hdrs - - -# exception raised when downloaded size does not match content-length -class ContentTooShortError(URLError): - def __init__(self, message, content): - URLError.__init__(self, message) - self.content = content diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/parse.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/parse.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/parse.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/parse.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,991 +0,0 @@ -""" -Ported using Python-Future from the Python 3.3 standard library. - -Parse (absolute and relative) URLs. - -urlparse module is based upon the following RFC specifications. - -RFC 3986 (STD66): "Uniform Resource Identifiers" by T. Berners-Lee, R. Fielding -and L. Masinter, January 2005. - -RFC 2732 : "Format for Literal IPv6 Addresses in URL's by R.Hinden, B.Carpenter -and L.Masinter, December 1999. - -RFC 2396: "Uniform Resource Identifiers (URI)": Generic Syntax by T. -Berners-Lee, R. Fielding, and L. Masinter, August 1998. - -RFC 2368: "The mailto URL scheme", by P.Hoffman , L Masinter, J. Zawinski, July 1998. - -RFC 1808: "Relative Uniform Resource Locators", by R. Fielding, UC Irvine, June -1995. - -RFC 1738: "Uniform Resource Locators (URL)" by T. Berners-Lee, L. Masinter, M. -McCahill, December 1994 - -RFC 3986 is considered the current standard and any future changes to -urlparse module should conform with it. The urlparse module is -currently not entirely compliant with this RFC due to defacto -scenarios for parsing, and for backward compatibility purposes, some -parsing quirks from older RFCs are retained. The testcases in -test_urlparse.py provides a good indicator of parsing behavior. -""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import bytes, chr, dict, int, range, str -from future.utils import raise_with_traceback - -import re -import sys -import collections - -__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag", - "urlsplit", "urlunsplit", "urlencode", "parse_qs", - "parse_qsl", "quote", "quote_plus", "quote_from_bytes", - "unquote", "unquote_plus", "unquote_to_bytes"] - -# A classification of schemes ('' means apply by default) -uses_relative = ['ftp', 'http', 'gopher', 'nntp', 'imap', - 'wais', 'file', 'https', 'shttp', 'mms', - 'prospero', 'rtsp', 'rtspu', '', 'sftp', - 'svn', 'svn+ssh'] -uses_netloc = ['ftp', 'http', 'gopher', 'nntp', 'telnet', - 'imap', 'wais', 'file', 'mms', 'https', 'shttp', - 'snews', 'prospero', 'rtsp', 'rtspu', 'rsync', '', - 'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh'] -uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap', - 'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips', - 'mms', '', 'sftp', 'tel'] - -# These are not actually used anymore, but should stay for backwards -# compatibility. (They are undocumented, but have a public-looking name.) -non_hierarchical = ['gopher', 'hdl', 'mailto', 'news', - 'telnet', 'wais', 'imap', 'snews', 'sip', 'sips'] -uses_query = ['http', 'wais', 'imap', 'https', 'shttp', 'mms', - 'gopher', 'rtsp', 'rtspu', 'sip', 'sips', ''] -uses_fragment = ['ftp', 'hdl', 'http', 'gopher', 'news', - 'nntp', 'wais', 'https', 'shttp', 'snews', - 'file', 'prospero', ''] - -# Characters valid in scheme names -scheme_chars = ('abcdefghijklmnopqrstuvwxyz' - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - '0123456789' - '+-.') - -# XXX: Consider replacing with functools.lru_cache -MAX_CACHE_SIZE = 20 -_parse_cache = {} - -def clear_cache(): - """Clear the parse cache and the quoters cache.""" - _parse_cache.clear() - _safe_quoters.clear() - - -# Helpers for bytes handling -# For 3.2, we deliberately require applications that -# handle improperly quoted URLs to do their own -# decoding and encoding. If valid use cases are -# presented, we may relax this by using latin-1 -# decoding internally for 3.3 -_implicit_encoding = 'ascii' -_implicit_errors = 'strict' - -def _noop(obj): - return obj - -def _encode_result(obj, encoding=_implicit_encoding, - errors=_implicit_errors): - return obj.encode(encoding, errors) - -def _decode_args(args, encoding=_implicit_encoding, - errors=_implicit_errors): - return tuple(x.decode(encoding, errors) if x else '' for x in args) - -def _coerce_args(*args): - # Invokes decode if necessary to create str args - # and returns the coerced inputs along with - # an appropriate result coercion function - # - noop for str inputs - # - encoding function otherwise - str_input = isinstance(args[0], str) - for arg in args[1:]: - # We special-case the empty string to support the - # "scheme=''" default argument to some functions - if arg and isinstance(arg, str) != str_input: - raise TypeError("Cannot mix str and non-str arguments") - if str_input: - return args + (_noop,) - return _decode_args(args) + (_encode_result,) - -# Result objects are more helpful than simple tuples -class _ResultMixinStr(object): - """Standard approach to encoding parsed results from str to bytes""" - __slots__ = () - - def encode(self, encoding='ascii', errors='strict'): - return self._encoded_counterpart(*(x.encode(encoding, errors) for x in self)) - - -class _ResultMixinBytes(object): - """Standard approach to decoding parsed results from bytes to str""" - __slots__ = () - - def decode(self, encoding='ascii', errors='strict'): - return self._decoded_counterpart(*(x.decode(encoding, errors) for x in self)) - - -class _NetlocResultMixinBase(object): - """Shared methods for the parsed result objects containing a netloc element""" - __slots__ = () - - @property - def username(self): - return self._userinfo[0] - - @property - def password(self): - return self._userinfo[1] - - @property - def hostname(self): - hostname = self._hostinfo[0] - if not hostname: - hostname = None - elif hostname is not None: - hostname = hostname.lower() - return hostname - - @property - def port(self): - port = self._hostinfo[1] - if port is not None: - port = int(port, 10) - # Return None on an illegal port - if not ( 0 <= port <= 65535): - return None - return port - - -class _NetlocResultMixinStr(_NetlocResultMixinBase, _ResultMixinStr): - __slots__ = () - - @property - def _userinfo(self): - netloc = self.netloc - userinfo, have_info, hostinfo = netloc.rpartition('@') - if have_info: - username, have_password, password = userinfo.partition(':') - if not have_password: - password = None - else: - username = password = None - return username, password - - @property - def _hostinfo(self): - netloc = self.netloc - _, _, hostinfo = netloc.rpartition('@') - _, have_open_br, bracketed = hostinfo.partition('[') - if have_open_br: - hostname, _, port = bracketed.partition(']') - _, have_port, port = port.partition(':') - else: - hostname, have_port, port = hostinfo.partition(':') - if not have_port: - port = None - return hostname, port - - -class _NetlocResultMixinBytes(_NetlocResultMixinBase, _ResultMixinBytes): - __slots__ = () - - @property - def _userinfo(self): - netloc = self.netloc - userinfo, have_info, hostinfo = netloc.rpartition(b'@') - if have_info: - username, have_password, password = userinfo.partition(b':') - if not have_password: - password = None - else: - username = password = None - return username, password - - @property - def _hostinfo(self): - netloc = self.netloc - _, _, hostinfo = netloc.rpartition(b'@') - _, have_open_br, bracketed = hostinfo.partition(b'[') - if have_open_br: - hostname, _, port = bracketed.partition(b']') - _, have_port, port = port.partition(b':') - else: - hostname, have_port, port = hostinfo.partition(b':') - if not have_port: - port = None - return hostname, port - - -from collections import namedtuple - -_DefragResultBase = namedtuple('DefragResult', 'url fragment') -_SplitResultBase = namedtuple('SplitResult', 'scheme netloc path query fragment') -_ParseResultBase = namedtuple('ParseResult', 'scheme netloc path params query fragment') - -# For backwards compatibility, alias _NetlocResultMixinStr -# ResultBase is no longer part of the documented API, but it is -# retained since deprecating it isn't worth the hassle -ResultBase = _NetlocResultMixinStr - -# Structured result objects for string data -class DefragResult(_DefragResultBase, _ResultMixinStr): - __slots__ = () - def geturl(self): - if self.fragment: - return self.url + '#' + self.fragment - else: - return self.url - -class SplitResult(_SplitResultBase, _NetlocResultMixinStr): - __slots__ = () - def geturl(self): - return urlunsplit(self) - -class ParseResult(_ParseResultBase, _NetlocResultMixinStr): - __slots__ = () - def geturl(self): - return urlunparse(self) - -# Structured result objects for bytes data -class DefragResultBytes(_DefragResultBase, _ResultMixinBytes): - __slots__ = () - def geturl(self): - if self.fragment: - return self.url + b'#' + self.fragment - else: - return self.url - -class SplitResultBytes(_SplitResultBase, _NetlocResultMixinBytes): - __slots__ = () - def geturl(self): - return urlunsplit(self) - -class ParseResultBytes(_ParseResultBase, _NetlocResultMixinBytes): - __slots__ = () - def geturl(self): - return urlunparse(self) - -# Set up the encode/decode result pairs -def _fix_result_transcoding(): - _result_pairs = ( - (DefragResult, DefragResultBytes), - (SplitResult, SplitResultBytes), - (ParseResult, ParseResultBytes), - ) - for _decoded, _encoded in _result_pairs: - _decoded._encoded_counterpart = _encoded - _encoded._decoded_counterpart = _decoded - -_fix_result_transcoding() -del _fix_result_transcoding - -def urlparse(url, scheme='', allow_fragments=True): - """Parse a URL into 6 components: - :///;?# - Return a 6-tuple: (scheme, netloc, path, params, query, fragment). - Note that we don't break the components up in smaller bits - (e.g. netloc is a single string) and we don't expand % escapes.""" - url, scheme, _coerce_result = _coerce_args(url, scheme) - splitresult = urlsplit(url, scheme, allow_fragments) - scheme, netloc, url, query, fragment = splitresult - if scheme in uses_params and ';' in url: - url, params = _splitparams(url) - else: - params = '' - result = ParseResult(scheme, netloc, url, params, query, fragment) - return _coerce_result(result) - -def _splitparams(url): - if '/' in url: - i = url.find(';', url.rfind('/')) - if i < 0: - return url, '' - else: - i = url.find(';') - return url[:i], url[i+1:] - -def _splitnetloc(url, start=0): - delim = len(url) # position of end of domain part of url, default is end - for c in '/?#': # look for delimiters; the order is NOT important - wdelim = url.find(c, start) # find first of this delim - if wdelim >= 0: # if found - delim = min(delim, wdelim) # use earliest delim position - return url[start:delim], url[delim:] # return (domain, rest) - -def urlsplit(url, scheme='', allow_fragments=True): - """Parse a URL into 5 components: - :///?# - Return a 5-tuple: (scheme, netloc, path, query, fragment). - Note that we don't break the components up in smaller bits - (e.g. netloc is a single string) and we don't expand % escapes.""" - url, scheme, _coerce_result = _coerce_args(url, scheme) - allow_fragments = bool(allow_fragments) - key = url, scheme, allow_fragments, type(url), type(scheme) - cached = _parse_cache.get(key, None) - if cached: - return _coerce_result(cached) - if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth - clear_cache() - netloc = query = fragment = '' - i = url.find(':') - if i > 0: - if url[:i] == 'http': # optimize the common case - scheme = url[:i].lower() - url = url[i+1:] - if url[:2] == '//': - netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return _coerce_result(v) - for c in url[:i]: - if c not in scheme_chars: - break - else: - # make sure "url" is not actually a port number (in which case - # "scheme" is really part of the path) - rest = url[i+1:] - if not rest or any(c not in '0123456789' for c in rest): - # not a port number - scheme, url = url[:i].lower(), rest - - if url[:2] == '//': - netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return _coerce_result(v) - -def urlunparse(components): - """Put a parsed URL back together again. This may result in a - slightly different, but equivalent URL, if the URL that was parsed - originally had redundant delimiters, e.g. a ? with an empty query - (the draft states that these are equivalent).""" - scheme, netloc, url, params, query, fragment, _coerce_result = ( - _coerce_args(*components)) - if params: - url = "%s;%s" % (url, params) - return _coerce_result(urlunsplit((scheme, netloc, url, query, fragment))) - -def urlunsplit(components): - """Combine the elements of a tuple as returned by urlsplit() into a - complete URL as a string. The data argument can be any five-item iterable. - This may result in a slightly different, but equivalent URL, if the URL that - was parsed originally had unnecessary delimiters (for example, a ? with an - empty query; the RFC states that these are equivalent).""" - scheme, netloc, url, query, fragment, _coerce_result = ( - _coerce_args(*components)) - if netloc or (scheme and scheme in uses_netloc and url[:2] != '//'): - if url and url[:1] != '/': url = '/' + url - url = '//' + (netloc or '') + url - if scheme: - url = scheme + ':' + url - if query: - url = url + '?' + query - if fragment: - url = url + '#' + fragment - return _coerce_result(url) - -def urljoin(base, url, allow_fragments=True): - """Join a base URL and a possibly relative URL to form an absolute - interpretation of the latter.""" - if not base: - return url - if not url: - return base - base, url, _coerce_result = _coerce_args(base, url) - bscheme, bnetloc, bpath, bparams, bquery, bfragment = \ - urlparse(base, '', allow_fragments) - scheme, netloc, path, params, query, fragment = \ - urlparse(url, bscheme, allow_fragments) - if scheme != bscheme or scheme not in uses_relative: - return _coerce_result(url) - if scheme in uses_netloc: - if netloc: - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - netloc = bnetloc - if path[:1] == '/': - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - if not path and not params: - path = bpath - params = bparams - if not query: - query = bquery - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - segments = bpath.split('/')[:-1] + path.split('/') - # XXX The stuff below is bogus in various ways... - if segments[-1] == '.': - segments[-1] = '' - while '.' in segments: - segments.remove('.') - while 1: - i = 1 - n = len(segments) - 1 - while i < n: - if (segments[i] == '..' - and segments[i-1] not in ('', '..')): - del segments[i-1:i+1] - break - i = i+1 - else: - break - if segments == ['', '..']: - segments[-1] = '' - elif len(segments) >= 2 and segments[-1] == '..': - segments[-2:] = [''] - return _coerce_result(urlunparse((scheme, netloc, '/'.join(segments), - params, query, fragment))) - -def urldefrag(url): - """Removes any existing fragment from URL. - - Returns a tuple of the defragmented URL and the fragment. If - the URL contained no fragments, the second element is the - empty string. - """ - url, _coerce_result = _coerce_args(url) - if '#' in url: - s, n, p, a, q, frag = urlparse(url) - defrag = urlunparse((s, n, p, a, q, '')) - else: - frag = '' - defrag = url - return _coerce_result(DefragResult(defrag, frag)) - -_hexdig = '0123456789ABCDEFabcdef' -_hextobyte = dict(((a + b).encode(), bytes([int(a + b, 16)])) - for a in _hexdig for b in _hexdig) - -def unquote_to_bytes(string): - """unquote_to_bytes('abc%20def') -> b'abc def'.""" - # Note: strings are encoded as UTF-8. This is only an issue if it contains - # unescaped non-ASCII characters, which URIs should not. - if not string: - # Is it a string-like object? - string.split - return bytes(b'') - if isinstance(string, str): - string = string.encode('utf-8') - ### For Python-Future: - # It is already a byte-string object, but force it to be newbytes here on - # Py2: - string = bytes(string) - ### - bits = string.split(b'%') - if len(bits) == 1: - return string - res = [bits[0]] - append = res.append - for item in bits[1:]: - try: - append(_hextobyte[item[:2]]) - append(item[2:]) - except KeyError: - append(b'%') - append(item) - return bytes(b'').join(res) - -_asciire = re.compile('([\x00-\x7f]+)') - -def unquote(string, encoding='utf-8', errors='replace'): - """Replace %xx escapes by their single-character equivalent. The optional - encoding and errors parameters specify how to decode percent-encoded - sequences into Unicode characters, as accepted by the bytes.decode() - method. - By default, percent-encoded sequences are decoded with UTF-8, and invalid - sequences are replaced by a placeholder character. - - unquote('abc%20def') -> 'abc def'. - """ - if '%' not in string: - string.split - return string - if encoding is None: - encoding = 'utf-8' - if errors is None: - errors = 'replace' - bits = _asciire.split(string) - res = [bits[0]] - append = res.append - for i in range(1, len(bits), 2): - append(unquote_to_bytes(bits[i]).decode(encoding, errors)) - append(bits[i + 1]) - return ''.join(res) - -def parse_qs(qs, keep_blank_values=False, strict_parsing=False, - encoding='utf-8', errors='replace'): - """Parse a query given as a string argument. - - Arguments: - - qs: percent-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - percent-encoded queries should be treated as blank strings. - A true value indicates that blanks should be retained as - blank strings. The default false value indicates that - blank values are to be ignored and treated as if they were - not included. - - strict_parsing: flag indicating what to do with parsing errors. - If false (the default), errors are silently ignored. - If true, errors raise a ValueError exception. - - encoding and errors: specify how to decode percent-encoded sequences - into Unicode characters, as accepted by the bytes.decode() method. - """ - parsed_result = {} - pairs = parse_qsl(qs, keep_blank_values, strict_parsing, - encoding=encoding, errors=errors) - for name, value in pairs: - if name in parsed_result: - parsed_result[name].append(value) - else: - parsed_result[name] = [value] - return parsed_result - -def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, - encoding='utf-8', errors='replace'): - """Parse a query given as a string argument. - - Arguments: - - qs: percent-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - percent-encoded queries should be treated as blank strings. A - true value indicates that blanks should be retained as blank - strings. The default false value indicates that blank values - are to be ignored and treated as if they were not included. - - strict_parsing: flag indicating what to do with parsing errors. If - false (the default), errors are silently ignored. If true, - errors raise a ValueError exception. - - encoding and errors: specify how to decode percent-encoded sequences - into Unicode characters, as accepted by the bytes.decode() method. - - Returns a list, as G-d intended. - """ - qs, _coerce_result = _coerce_args(qs) - pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] - r = [] - for name_value in pairs: - if not name_value and not strict_parsing: - continue - nv = name_value.split('=', 1) - if len(nv) != 2: - if strict_parsing: - raise ValueError("bad query field: %r" % (name_value,)) - # Handle case of a control-name with no equal sign - if keep_blank_values: - nv.append('') - else: - continue - if len(nv[1]) or keep_blank_values: - name = nv[0].replace('+', ' ') - name = unquote(name, encoding=encoding, errors=errors) - name = _coerce_result(name) - value = nv[1].replace('+', ' ') - value = unquote(value, encoding=encoding, errors=errors) - value = _coerce_result(value) - r.append((name, value)) - return r - -def unquote_plus(string, encoding='utf-8', errors='replace'): - """Like unquote(), but also replace plus signs by spaces, as required for - unquoting HTML form values. - - unquote_plus('%7e/abc+def') -> '~/abc def' - """ - string = string.replace('+', ' ') - return unquote(string, encoding, errors) - -_ALWAYS_SAFE = frozenset(bytes(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - b'abcdefghijklmnopqrstuvwxyz' - b'0123456789' - b'_.-')) -_ALWAYS_SAFE_BYTES = bytes(_ALWAYS_SAFE) -_safe_quoters = {} - -class Quoter(collections.defaultdict): - """A mapping from bytes (in range(0,256)) to strings. - - String values are percent-encoded byte values, unless the key < 128, and - in the "safe" set (either the specified safe set, or default set). - """ - # Keeps a cache internally, using defaultdict, for efficiency (lookups - # of cached keys don't call Python code at all). - def __init__(self, safe): - """safe: bytes object.""" - self.safe = _ALWAYS_SAFE.union(bytes(safe)) - - def __repr__(self): - # Without this, will just display as a defaultdict - return "" % dict(self) - - def __missing__(self, b): - # Handle a cache miss. Store quoted string in cache and return. - res = chr(b) if b in self.safe else '%{0:02X}'.format(b) - self[b] = res - return res - -def quote(string, safe='/', encoding=None, errors=None): - """quote('abc def') -> 'abc%20def' - - Each part of a URL, e.g. the path info, the query, etc., has a - different set of reserved characters that must be quoted. - - RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists - the following reserved characters. - - reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | - "$" | "," - - Each of these characters is reserved in some component of a URL, - but not necessarily in all of them. - - By default, the quote function is intended for quoting the path - section of a URL. Thus, it will not encode '/'. This character - is reserved, but in typical usage the quote function is being - called on a path where the existing slash characters are used as - reserved characters. - - string and safe may be either str or bytes objects. encoding must - not be specified if string is a str. - - The optional encoding and errors parameters specify how to deal with - non-ASCII characters, as accepted by the str.encode method. - By default, encoding='utf-8' (characters are encoded with UTF-8), and - errors='strict' (unsupported characters raise a UnicodeEncodeError). - """ - if isinstance(string, str): - if not string: - return string - if encoding is None: - encoding = 'utf-8' - if errors is None: - errors = 'strict' - string = string.encode(encoding, errors) - else: - if encoding is not None: - raise TypeError("quote() doesn't support 'encoding' for bytes") - if errors is not None: - raise TypeError("quote() doesn't support 'errors' for bytes") - return quote_from_bytes(string, safe) - -def quote_plus(string, safe='', encoding=None, errors=None): - """Like quote(), but also replace ' ' with '+', as required for quoting - HTML form values. Plus signs in the original string are escaped unless - they are included in safe. It also does not have safe default to '/'. - """ - # Check if ' ' in string, where string may either be a str or bytes. If - # there are no spaces, the regular quote will produce the right answer. - if ((isinstance(string, str) and ' ' not in string) or - (isinstance(string, bytes) and b' ' not in string)): - return quote(string, safe, encoding, errors) - if isinstance(safe, str): - space = str(' ') - else: - space = bytes(b' ') - string = quote(string, safe + space, encoding, errors) - return string.replace(' ', '+') - -def quote_from_bytes(bs, safe='/'): - """Like quote(), but accepts a bytes object rather than a str, and does - not perform string-to-bytes encoding. It always returns an ASCII string. - quote_from_bytes(b'abc def\x3f') -> 'abc%20def%3f' - """ - if not isinstance(bs, (bytes, bytearray)): - raise TypeError("quote_from_bytes() expected bytes") - if not bs: - return str('') - ### For Python-Future: - bs = bytes(bs) - ### - if isinstance(safe, str): - # Normalize 'safe' by converting to bytes and removing non-ASCII chars - safe = str(safe).encode('ascii', 'ignore') - else: - ### For Python-Future: - safe = bytes(safe) - ### - safe = bytes([c for c in safe if c < 128]) - if not bs.rstrip(_ALWAYS_SAFE_BYTES + safe): - return bs.decode() - try: - quoter = _safe_quoters[safe] - except KeyError: - _safe_quoters[safe] = quoter = Quoter(safe).__getitem__ - return str('').join([quoter(char) for char in bs]) - -def urlencode(query, doseq=False, safe='', encoding=None, errors=None): - """Encode a sequence of two-element tuples or dictionary into a URL query string. - - If any values in the query arg are sequences and doseq is true, each - sequence element is converted to a separate parameter. - - If the query arg is a sequence of two-element tuples, the order of the - parameters in the output will match the order of parameters in the - input. - - The query arg may be either a string or a bytes type. When query arg is a - string, the safe, encoding and error parameters are sent the quote_plus for - encoding. - """ - - if hasattr(query, "items"): - query = query.items() - else: - # It's a bother at times that strings and string-like objects are - # sequences. - try: - # non-sequence items should not work with len() - # non-empty strings will fail this - if len(query) and not isinstance(query[0], tuple): - raise TypeError - # Zero-length sequences of all types will get here and succeed, - # but that's a minor nit. Since the original implementation - # allowed empty dicts that type of behavior probably should be - # preserved for consistency - except TypeError: - ty, va, tb = sys.exc_info() - raise_with_traceback(TypeError("not a valid non-string sequence " - "or mapping object"), tb) - - l = [] - if not doseq: - for k, v in query: - if isinstance(k, bytes): - k = quote_plus(k, safe) - else: - k = quote_plus(str(k), safe, encoding, errors) - - if isinstance(v, bytes): - v = quote_plus(v, safe) - else: - v = quote_plus(str(v), safe, encoding, errors) - l.append(k + '=' + v) - else: - for k, v in query: - if isinstance(k, bytes): - k = quote_plus(k, safe) - else: - k = quote_plus(str(k), safe, encoding, errors) - - if isinstance(v, bytes): - v = quote_plus(v, safe) - l.append(k + '=' + v) - elif isinstance(v, str): - v = quote_plus(v, safe, encoding, errors) - l.append(k + '=' + v) - else: - try: - # Is this a sufficient test for sequence-ness? - x = len(v) - except TypeError: - # not a sequence - v = quote_plus(str(v), safe, encoding, errors) - l.append(k + '=' + v) - else: - # loop over the sequence - for elt in v: - if isinstance(elt, bytes): - elt = quote_plus(elt, safe) - else: - elt = quote_plus(str(elt), safe, encoding, errors) - l.append(k + '=' + elt) - return str('&').join(l) - -# Utilities to parse URLs (most of these return None for missing parts): -# unwrap('') --> 'type://host/path' -# splittype('type:opaquestring') --> 'type', 'opaquestring' -# splithost('//host[:port]/path') --> 'host[:port]', '/path' -# splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]' -# splitpasswd('user:passwd') -> 'user', 'passwd' -# splitport('host:port') --> 'host', 'port' -# splitquery('/path?query') --> '/path', 'query' -# splittag('/path#tag') --> '/path', 'tag' -# splitattr('/path;attr1=value1;attr2=value2;...') -> -# '/path', ['attr1=value1', 'attr2=value2', ...] -# splitvalue('attr=value') --> 'attr', 'value' -# urllib.parse.unquote('abc%20def') -> 'abc def' -# quote('abc def') -> 'abc%20def') - -def to_bytes(url): - """to_bytes(u"URL") --> 'URL'.""" - # Most URL schemes require ASCII. If that changes, the conversion - # can be relaxed. - # XXX get rid of to_bytes() - if isinstance(url, str): - try: - url = url.encode("ASCII").decode() - except UnicodeError: - raise UnicodeError("URL " + repr(url) + - " contains non-ASCII characters") - return url - -def unwrap(url): - """unwrap('') --> 'type://host/path'.""" - url = str(url).strip() - if url[:1] == '<' and url[-1:] == '>': - url = url[1:-1].strip() - if url[:4] == 'URL:': url = url[4:].strip() - return url - -_typeprog = None -def splittype(url): - """splittype('type:opaquestring') --> 'type', 'opaquestring'.""" - global _typeprog - if _typeprog is None: - import re - _typeprog = re.compile('^([^/:]+):') - - match = _typeprog.match(url) - if match: - scheme = match.group(1) - return scheme.lower(), url[len(scheme) + 1:] - return None, url - -_hostprog = None -def splithost(url): - """splithost('//host[:port]/path') --> 'host[:port]', '/path'.""" - global _hostprog - if _hostprog is None: - import re - _hostprog = re.compile('^//([^/?]*)(.*)$') - - match = _hostprog.match(url) - if match: - host_port = match.group(1) - path = match.group(2) - if path and not path.startswith('/'): - path = '/' + path - return host_port, path - return None, url - -_userprog = None -def splituser(host): - """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" - global _userprog - if _userprog is None: - import re - _userprog = re.compile('^(.*)@(.*)$') - - match = _userprog.match(host) - if match: return match.group(1, 2) - return None, host - -_passwdprog = None -def splitpasswd(user): - """splitpasswd('user:passwd') -> 'user', 'passwd'.""" - global _passwdprog - if _passwdprog is None: - import re - _passwdprog = re.compile('^([^:]*):(.*)$',re.S) - - match = _passwdprog.match(user) - if match: return match.group(1, 2) - return user, None - -# splittag('/path#tag') --> '/path', 'tag' -_portprog = None -def splitport(host): - """splitport('host:port') --> 'host', 'port'.""" - global _portprog - if _portprog is None: - import re - _portprog = re.compile('^(.*):([0-9]+)$') - - match = _portprog.match(host) - if match: return match.group(1, 2) - return host, None - -_nportprog = None -def splitnport(host, defport=-1): - """Split host and port, returning numeric port. - Return given default port if no ':' found; defaults to -1. - Return numerical port if a valid number are found after ':'. - Return None if ':' but not a valid number.""" - global _nportprog - if _nportprog is None: - import re - _nportprog = re.compile('^(.*):(.*)$') - - match = _nportprog.match(host) - if match: - host, port = match.group(1, 2) - try: - if not port: raise ValueError("no digits") - nport = int(port) - except ValueError: - nport = None - return host, nport - return host, defport - -_queryprog = None -def splitquery(url): - """splitquery('/path?query') --> '/path', 'query'.""" - global _queryprog - if _queryprog is None: - import re - _queryprog = re.compile('^(.*)\?([^?]*)$') - - match = _queryprog.match(url) - if match: return match.group(1, 2) - return url, None - -_tagprog = None -def splittag(url): - """splittag('/path#tag') --> '/path', 'tag'.""" - global _tagprog - if _tagprog is None: - import re - _tagprog = re.compile('^(.*)#([^#]*)$') - - match = _tagprog.match(url) - if match: return match.group(1, 2) - return url, None - -def splitattr(url): - """splitattr('/path;attr1=value1;attr2=value2;...') -> - '/path', ['attr1=value1', 'attr2=value2', ...].""" - words = url.split(';') - return words[0], words[1:] - -_valueprog = None -def splitvalue(attr): - """splitvalue('attr=value') --> 'attr', 'value'.""" - global _valueprog - if _valueprog is None: - import re - _valueprog = re.compile('^([^=]*)=(.*)$') - - match = _valueprog.match(attr) - if match: return match.group(1, 2) - return attr, None diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/request.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/request.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/request.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/request.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2641 +0,0 @@ -""" -Ported using Python-Future from the Python 3.3 standard library. - -An extensible library for opening URLs using a variety of protocols - -The simplest way to use this module is to call the urlopen function, -which accepts a string containing a URL or a Request object (described -below). It opens the URL and returns the results as file-like -object; the returned object has some extra methods described below. - -The OpenerDirector manages a collection of Handler objects that do -all the actual work. Each Handler implements a particular protocol or -option. The OpenerDirector is a composite object that invokes the -Handlers needed to open the requested URL. For example, the -HTTPHandler performs HTTP GET and POST requests and deals with -non-error returns. The HTTPRedirectHandler automatically deals with -HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler -deals with digest authentication. - -urlopen(url, data=None) -- Basic usage is the same as original -urllib. pass the url and optionally data to post to an HTTP URL, and -get a file-like object back. One difference is that you can also pass -a Request instance instead of URL. Raises a URLError (subclass of -IOError); for HTTP errors, raises an HTTPError, which can also be -treated as a valid response. - -build_opener -- Function that creates a new OpenerDirector instance. -Will install the default handlers. Accepts one or more Handlers as -arguments, either instances or Handler classes that it will -instantiate. If one of the argument is a subclass of the default -handler, the argument will be installed instead of the default. - -install_opener -- Installs a new opener as the default opener. - -objects of interest: - -OpenerDirector -- Sets up the User Agent as the Python-urllib client and manages -the Handler classes, while dealing with requests and responses. - -Request -- An object that encapsulates the state of a request. The -state can be as simple as the URL. It can also include extra HTTP -headers, e.g. a User-Agent. - -BaseHandler -- - -internals: -BaseHandler and parent -_call_chain conventions - -Example usage: - -import urllib.request - -# set up authentication info -authinfo = urllib.request.HTTPBasicAuthHandler() -authinfo.add_password(realm='PDQ Application', - uri='https://mahler:8092/site-updates.py', - user='klem', - passwd='geheim$parole') - -proxy_support = urllib.request.ProxyHandler({"http" : "http://ahad-haam:3128"}) - -# build a new opener that adds authentication and caching FTP handlers -opener = urllib.request.build_opener(proxy_support, authinfo, - urllib.request.CacheFTPHandler) - -# install it -urllib.request.install_opener(opener) - -f = urllib.request.urlopen('http://www.python.org/') -""" - -# XXX issues: -# If an authentication error handler that tries to perform -# authentication for some reason but fails, how should the error be -# signalled? The client needs to know the HTTP error code. But if -# the handler knows that the problem was, e.g., that it didn't know -# that hash algo that requested in the challenge, it would be good to -# pass that information along to the client, too. -# ftp errors aren't handled cleanly -# check digest against correct (i.e. non-apache) implementation - -# Possible extensions: -# complex proxies XXX not sure what exactly was meant by this -# abstract factory for opener - -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import bytes, dict, filter, input, int, map, open, str -from future.utils import PY2, PY3, raise_with_traceback - -import base64 -import bisect -import hashlib -import array - -from future.backports import email -from future.backports.http import client as http_client -from .error import URLError, HTTPError, ContentTooShortError -from .parse import ( - urlparse, urlsplit, urljoin, unwrap, quote, unquote, - splittype, splithost, splitport, splituser, splitpasswd, - splitattr, splitquery, splitvalue, splittag, to_bytes, urlunparse) -from .response import addinfourl, addclosehook - -import io -import os -import posixpath -import re -import socket -import sys -import time -import collections -import tempfile -import contextlib -import warnings - -# check for SSL -try: - import ssl - # Not available in the SSL module in Py2: - from ssl import SSLContext -except ImportError: - _have_ssl = False -else: - _have_ssl = True - -__all__ = [ - # Classes - 'Request', 'OpenerDirector', 'BaseHandler', 'HTTPDefaultErrorHandler', - 'HTTPRedirectHandler', 'HTTPCookieProcessor', 'ProxyHandler', - 'HTTPPasswordMgr', 'HTTPPasswordMgrWithDefaultRealm', - 'AbstractBasicAuthHandler', 'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler', - 'AbstractDigestAuthHandler', 'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler', - 'HTTPHandler', 'FileHandler', 'FTPHandler', 'CacheFTPHandler', - 'UnknownHandler', 'HTTPErrorProcessor', - # Functions - 'urlopen', 'install_opener', 'build_opener', - 'pathname2url', 'url2pathname', 'getproxies', - # Legacy interface - 'urlretrieve', 'urlcleanup', 'URLopener', 'FancyURLopener', -] - -# used in User-Agent header sent -__version__ = sys.version[:3] - -_opener = None -def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **_3to2kwargs): - if 'cadefault' in _3to2kwargs: cadefault = _3to2kwargs['cadefault']; del _3to2kwargs['cadefault'] - else: cadefault = False - if 'capath' in _3to2kwargs: capath = _3to2kwargs['capath']; del _3to2kwargs['capath'] - else: capath = None - if 'cafile' in _3to2kwargs: cafile = _3to2kwargs['cafile']; del _3to2kwargs['cafile'] - else: cafile = None - global _opener - if cafile or capath or cadefault: - if not _have_ssl: - raise ValueError('SSL support not available') - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - context.verify_mode = ssl.CERT_REQUIRED - if cafile or capath: - context.load_verify_locations(cafile, capath) - else: - context.set_default_verify_paths() - https_handler = HTTPSHandler(context=context, check_hostname=True) - opener = build_opener(https_handler) - elif _opener is None: - _opener = opener = build_opener() - else: - opener = _opener - return opener.open(url, data, timeout) - -def install_opener(opener): - global _opener - _opener = opener - -_url_tempfiles = [] -def urlretrieve(url, filename=None, reporthook=None, data=None): - """ - Retrieve a URL into a temporary location on disk. - - Requires a URL argument. If a filename is passed, it is used as - the temporary file location. The reporthook argument should be - a callable that accepts a block number, a read size, and the - total file size of the URL target. The data argument should be - valid URL encoded data. - - If a filename is passed and the URL points to a local resource, - the result is a copy from local file to new file. - - Returns a tuple containing the path to the newly created - data file as well as the resulting HTTPMessage object. - """ - url_type, path = splittype(url) - - with contextlib.closing(urlopen(url, data)) as fp: - headers = fp.info() - - # Just return the local path and the "headers" for file:// - # URLs. No sense in performing a copy unless requested. - if url_type == "file" and not filename: - return os.path.normpath(path), headers - - # Handle temporary file setup. - if filename: - tfp = open(filename, 'wb') - else: - tfp = tempfile.NamedTemporaryFile(delete=False) - filename = tfp.name - _url_tempfiles.append(filename) - - with tfp: - result = filename, headers - bs = 1024*8 - size = -1 - read = 0 - blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) - - if reporthook: - reporthook(blocknum, bs, size) - - while True: - block = fp.read(bs) - if not block: - break - read += len(block) - tfp.write(block) - blocknum += 1 - if reporthook: - reporthook(blocknum, bs, size) - - if size >= 0 and read < size: - raise ContentTooShortError( - "retrieval incomplete: got only %i out of %i bytes" - % (read, size), result) - - return result - -def urlcleanup(): - for temp_file in _url_tempfiles: - try: - os.unlink(temp_file) - except EnvironmentError: - pass - - del _url_tempfiles[:] - global _opener - if _opener: - _opener = None - -if PY3: - _cut_port_re = re.compile(r":\d+$", re.ASCII) -else: - _cut_port_re = re.compile(r":\d+$") - -def request_host(request): - - """Return request-host, as defined by RFC 2965. - - Variation from RFC: returned value is lowercased, for convenient - comparison. - - """ - url = request.full_url - host = urlparse(url)[1] - if host == "": - host = request.get_header("Host", "") - - # remove port, if present - host = _cut_port_re.sub("", host, 1) - return host.lower() - -class Request(object): - - def __init__(self, url, data=None, headers={}, - origin_req_host=None, unverifiable=False, - method=None): - # unwrap('') --> 'type://host/path' - self.full_url = unwrap(url) - self.full_url, self.fragment = splittag(self.full_url) - self.data = data - self.headers = {} - self._tunnel_host = None - for key, value in headers.items(): - self.add_header(key, value) - self.unredirected_hdrs = {} - if origin_req_host is None: - origin_req_host = request_host(self) - self.origin_req_host = origin_req_host - self.unverifiable = unverifiable - self.method = method - self._parse() - - def _parse(self): - self.type, rest = splittype(self.full_url) - if self.type is None: - raise ValueError("unknown url type: %r" % self.full_url) - self.host, self.selector = splithost(rest) - if self.host: - self.host = unquote(self.host) - - def get_method(self): - """Return a string indicating the HTTP request method.""" - if self.method is not None: - return self.method - elif self.data is not None: - return "POST" - else: - return "GET" - - def get_full_url(self): - if self.fragment: - return '%s#%s' % (self.full_url, self.fragment) - else: - return self.full_url - - # Begin deprecated methods - - def add_data(self, data): - msg = "Request.add_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - self.data = data - - def has_data(self): - msg = "Request.has_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.data is not None - - def get_data(self): - msg = "Request.get_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.data - - def get_type(self): - msg = "Request.get_type method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.type - - def get_host(self): - msg = "Request.get_host method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.host - - def get_selector(self): - msg = "Request.get_selector method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.selector - - def is_unverifiable(self): - msg = "Request.is_unverifiable method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.unverifiable - - def get_origin_req_host(self): - msg = "Request.get_origin_req_host method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.origin_req_host - - # End deprecated methods - - def set_proxy(self, host, type): - if self.type == 'https' and not self._tunnel_host: - self._tunnel_host = self.host - else: - self.type= type - self.selector = self.full_url - self.host = host - - def has_proxy(self): - return self.selector == self.full_url - - def add_header(self, key, val): - # useful for something like authentication - self.headers[key.capitalize()] = val - - def add_unredirected_header(self, key, val): - # will not be added to a redirected request - self.unredirected_hdrs[key.capitalize()] = val - - def has_header(self, header_name): - return (header_name in self.headers or - header_name in self.unredirected_hdrs) - - def get_header(self, header_name, default=None): - return self.headers.get( - header_name, - self.unredirected_hdrs.get(header_name, default)) - - def header_items(self): - hdrs = self.unredirected_hdrs.copy() - hdrs.update(self.headers) - return list(hdrs.items()) - -class OpenerDirector(object): - def __init__(self): - client_version = "Python-urllib/%s" % __version__ - self.addheaders = [('User-agent', client_version)] - # self.handlers is retained only for backward compatibility - self.handlers = [] - # manage the individual handlers - self.handle_open = {} - self.handle_error = {} - self.process_response = {} - self.process_request = {} - - def add_handler(self, handler): - if not hasattr(handler, "add_parent"): - raise TypeError("expected BaseHandler instance, got %r" % - type(handler)) - - added = False - for meth in dir(handler): - if meth in ["redirect_request", "do_open", "proxy_open"]: - # oops, coincidental match - continue - - i = meth.find("_") - protocol = meth[:i] - condition = meth[i+1:] - - if condition.startswith("error"): - j = condition.find("_") + i + 1 - kind = meth[j+1:] - try: - kind = int(kind) - except ValueError: - pass - lookup = self.handle_error.get(protocol, {}) - self.handle_error[protocol] = lookup - elif condition == "open": - kind = protocol - lookup = self.handle_open - elif condition == "response": - kind = protocol - lookup = self.process_response - elif condition == "request": - kind = protocol - lookup = self.process_request - else: - continue - - handlers = lookup.setdefault(kind, []) - if handlers: - bisect.insort(handlers, handler) - else: - handlers.append(handler) - added = True - - if added: - bisect.insort(self.handlers, handler) - handler.add_parent(self) - - def close(self): - # Only exists for backwards compatibility. - pass - - def _call_chain(self, chain, kind, meth_name, *args): - # Handlers raise an exception if no one else should try to handle - # the request, or return None if they can't but another handler - # could. Otherwise, they return the response. - handlers = chain.get(kind, ()) - for handler in handlers: - func = getattr(handler, meth_name) - result = func(*args) - if result is not None: - return result - - def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - """ - Accept a URL or a Request object - - Python-Future: if the URL is passed as a byte-string, decode it first. - """ - if isinstance(fullurl, bytes): - fullurl = fullurl.decode() - if isinstance(fullurl, str): - req = Request(fullurl, data) - else: - req = fullurl - if data is not None: - req.data = data - - req.timeout = timeout - protocol = req.type - - # pre-process request - meth_name = protocol+"_request" - for processor in self.process_request.get(protocol, []): - meth = getattr(processor, meth_name) - req = meth(req) - - response = self._open(req, data) - - # post-process response - meth_name = protocol+"_response" - for processor in self.process_response.get(protocol, []): - meth = getattr(processor, meth_name) - response = meth(req, response) - - return response - - def _open(self, req, data=None): - result = self._call_chain(self.handle_open, 'default', - 'default_open', req) - if result: - return result - - protocol = req.type - result = self._call_chain(self.handle_open, protocol, protocol + - '_open', req) - if result: - return result - - return self._call_chain(self.handle_open, 'unknown', - 'unknown_open', req) - - def error(self, proto, *args): - if proto in ('http', 'https'): - # XXX http[s] protocols are special-cased - dict = self.handle_error['http'] # https is not different than http - proto = args[2] # YUCK! - meth_name = 'http_error_%s' % proto - http_err = 1 - orig_args = args - else: - dict = self.handle_error - meth_name = proto + '_error' - http_err = 0 - args = (dict, proto, meth_name) + args - result = self._call_chain(*args) - if result: - return result - - if http_err: - args = (dict, 'default', 'http_error_default') + orig_args - return self._call_chain(*args) - -# XXX probably also want an abstract factory that knows when it makes -# sense to skip a superclass in favor of a subclass and when it might -# make sense to include both - -def build_opener(*handlers): - """Create an opener object from a list of handlers. - - The opener will use several default handlers, including support - for HTTP, FTP and when applicable HTTPS. - - If any of the handlers passed as arguments are subclasses of the - default handlers, the default handlers will not be used. - """ - def isclass(obj): - return isinstance(obj, type) or hasattr(obj, "__bases__") - - opener = OpenerDirector() - default_classes = [ProxyHandler, UnknownHandler, HTTPHandler, - HTTPDefaultErrorHandler, HTTPRedirectHandler, - FTPHandler, FileHandler, HTTPErrorProcessor] - if hasattr(http_client, "HTTPSConnection"): - default_classes.append(HTTPSHandler) - skip = set() - for klass in default_classes: - for check in handlers: - if isclass(check): - if issubclass(check, klass): - skip.add(klass) - elif isinstance(check, klass): - skip.add(klass) - for klass in skip: - default_classes.remove(klass) - - for klass in default_classes: - opener.add_handler(klass()) - - for h in handlers: - if isclass(h): - h = h() - opener.add_handler(h) - return opener - -class BaseHandler(object): - handler_order = 500 - - def add_parent(self, parent): - self.parent = parent - - def close(self): - # Only exists for backwards compatibility - pass - - def __lt__(self, other): - if not hasattr(other, "handler_order"): - # Try to preserve the old behavior of having custom classes - # inserted after default ones (works only for custom user - # classes which are not aware of handler_order). - return True - return self.handler_order < other.handler_order - - -class HTTPErrorProcessor(BaseHandler): - """Process HTTP error responses.""" - handler_order = 1000 # after all other processing - - def http_response(self, request, response): - code, msg, hdrs = response.code, response.msg, response.info() - - # According to RFC 2616, "2xx" code indicates that the client's - # request was successfully received, understood, and accepted. - if not (200 <= code < 300): - response = self.parent.error( - 'http', request, response, code, msg, hdrs) - - return response - - https_response = http_response - -class HTTPDefaultErrorHandler(BaseHandler): - def http_error_default(self, req, fp, code, msg, hdrs): - raise HTTPError(req.full_url, code, msg, hdrs, fp) - -class HTTPRedirectHandler(BaseHandler): - # maximum number of redirections to any single URL - # this is needed because of the state that cookies introduce - max_repeats = 4 - # maximum total number of redirections (regardless of URL) before - # assuming we're in a loop - max_redirections = 10 - - def redirect_request(self, req, fp, code, msg, headers, newurl): - """Return a Request or None in response to a redirect. - - This is called by the http_error_30x methods when a - redirection response is received. If a redirection should - take place, return a new Request to allow http_error_30x to - perform the redirect. Otherwise, raise HTTPError if no-one - else should try to handle this url. Return None if you can't - but another Handler might. - """ - m = req.get_method() - if (not (code in (301, 302, 303, 307) and m in ("GET", "HEAD") - or code in (301, 302, 303) and m == "POST")): - raise HTTPError(req.full_url, code, msg, headers, fp) - - # Strictly (according to RFC 2616), 301 or 302 in response to - # a POST MUST NOT cause a redirection without confirmation - # from the user (of urllib.request, in this case). In practice, - # essentially all clients do redirect in this case, so we do - # the same. - # be conciliant with URIs containing a space - newurl = newurl.replace(' ', '%20') - CONTENT_HEADERS = ("content-length", "content-type") - newheaders = dict((k, v) for k, v in req.headers.items() - if k.lower() not in CONTENT_HEADERS) - return Request(newurl, - headers=newheaders, - origin_req_host=req.origin_req_host, - unverifiable=True) - - # Implementation note: To avoid the server sending us into an - # infinite loop, the request object needs to track what URLs we - # have already seen. Do this by adding a handler-specific - # attribute to the Request object. - def http_error_302(self, req, fp, code, msg, headers): - # Some servers (incorrectly) return multiple Location headers - # (so probably same goes for URI). Use first header. - if "location" in headers: - newurl = headers["location"] - elif "uri" in headers: - newurl = headers["uri"] - else: - return - - # fix a possible malformed URL - urlparts = urlparse(newurl) - - # For security reasons we don't allow redirection to anything other - # than http, https or ftp. - - if urlparts.scheme not in ('http', 'https', 'ftp', ''): - raise HTTPError( - newurl, code, - "%s - Redirection to url '%s' is not allowed" % (msg, newurl), - headers, fp) - - if not urlparts.path: - urlparts = list(urlparts) - urlparts[2] = "/" - newurl = urlunparse(urlparts) - - newurl = urljoin(req.full_url, newurl) - - # XXX Probably want to forget about the state of the current - # request, although that might interact poorly with other - # handlers that also use handler-specific request attributes - new = self.redirect_request(req, fp, code, msg, headers, newurl) - if new is None: - return - - # loop detection - # .redirect_dict has a key url if url was previously visited. - if hasattr(req, 'redirect_dict'): - visited = new.redirect_dict = req.redirect_dict - if (visited.get(newurl, 0) >= self.max_repeats or - len(visited) >= self.max_redirections): - raise HTTPError(req.full_url, code, - self.inf_msg + msg, headers, fp) - else: - visited = new.redirect_dict = req.redirect_dict = {} - visited[newurl] = visited.get(newurl, 0) + 1 - - # Don't close the fp until we are sure that we won't use it - # with HTTPError. - fp.read() - fp.close() - - return self.parent.open(new, timeout=req.timeout) - - http_error_301 = http_error_303 = http_error_307 = http_error_302 - - inf_msg = "The HTTP server returned a redirect error that would " \ - "lead to an infinite loop.\n" \ - "The last 30x error message was:\n" - - -def _parse_proxy(proxy): - """Return (scheme, user, password, host/port) given a URL or an authority. - - If a URL is supplied, it must have an authority (host:port) component. - According to RFC 3986, having an authority component means the URL must - have two slashes after the scheme: - - >>> _parse_proxy('file:/ftp.example.com/') - Traceback (most recent call last): - ValueError: proxy URL with no authority: 'file:/ftp.example.com/' - - The first three items of the returned tuple may be None. - - Examples of authority parsing: - - >>> _parse_proxy('proxy.example.com') - (None, None, None, 'proxy.example.com') - >>> _parse_proxy('proxy.example.com:3128') - (None, None, None, 'proxy.example.com:3128') - - The authority component may optionally include userinfo (assumed to be - username:password): - - >>> _parse_proxy('joe:password@proxy.example.com') - (None, 'joe', 'password', 'proxy.example.com') - >>> _parse_proxy('joe:password@proxy.example.com:3128') - (None, 'joe', 'password', 'proxy.example.com:3128') - - Same examples, but with URLs instead: - - >>> _parse_proxy('http://proxy.example.com/') - ('http', None, None, 'proxy.example.com') - >>> _parse_proxy('http://proxy.example.com:3128/') - ('http', None, None, 'proxy.example.com:3128') - >>> _parse_proxy('http://joe:password@proxy.example.com/') - ('http', 'joe', 'password', 'proxy.example.com') - >>> _parse_proxy('http://joe:password@proxy.example.com:3128') - ('http', 'joe', 'password', 'proxy.example.com:3128') - - Everything after the authority is ignored: - - >>> _parse_proxy('ftp://joe:password@proxy.example.com/rubbish:3128') - ('ftp', 'joe', 'password', 'proxy.example.com') - - Test for no trailing '/' case: - - >>> _parse_proxy('http://joe:password@proxy.example.com') - ('http', 'joe', 'password', 'proxy.example.com') - - """ - scheme, r_scheme = splittype(proxy) - if not r_scheme.startswith("/"): - # authority - scheme = None - authority = proxy - else: - # URL - if not r_scheme.startswith("//"): - raise ValueError("proxy URL with no authority: %r" % proxy) - # We have an authority, so for RFC 3986-compliant URLs (by ss 3. - # and 3.3.), path is empty or starts with '/' - end = r_scheme.find("/", 2) - if end == -1: - end = None - authority = r_scheme[2:end] - userinfo, hostport = splituser(authority) - if userinfo is not None: - user, password = splitpasswd(userinfo) - else: - user = password = None - return scheme, user, password, hostport - -class ProxyHandler(BaseHandler): - # Proxies must be in front - handler_order = 100 - - def __init__(self, proxies=None): - if proxies is None: - proxies = getproxies() - assert hasattr(proxies, 'keys'), "proxies must be a mapping" - self.proxies = proxies - for type, url in proxies.items(): - setattr(self, '%s_open' % type, - lambda r, proxy=url, type=type, meth=self.proxy_open: - meth(r, proxy, type)) - - def proxy_open(self, req, proxy, type): - orig_type = req.type - proxy_type, user, password, hostport = _parse_proxy(proxy) - if proxy_type is None: - proxy_type = orig_type - - if req.host and proxy_bypass(req.host): - return None - - if user and password: - user_pass = '%s:%s' % (unquote(user), - unquote(password)) - creds = base64.b64encode(user_pass.encode()).decode("ascii") - req.add_header('Proxy-authorization', 'Basic ' + creds) - hostport = unquote(hostport) - req.set_proxy(hostport, proxy_type) - if orig_type == proxy_type or orig_type == 'https': - # let other handlers take care of it - return None - else: - # need to start over, because the other handlers don't - # grok the proxy's URL type - # e.g. if we have a constructor arg proxies like so: - # {'http': 'ftp://proxy.example.com'}, we may end up turning - # a request for http://acme.example.com/a into one for - # ftp://proxy.example.com/a - return self.parent.open(req, timeout=req.timeout) - -class HTTPPasswordMgr(object): - - def __init__(self): - self.passwd = {} - - def add_password(self, realm, uri, user, passwd): - # uri could be a single URI or a sequence - if isinstance(uri, str): - uri = [uri] - if realm not in self.passwd: - self.passwd[realm] = {} - for default_port in True, False: - reduced_uri = tuple( - [self.reduce_uri(u, default_port) for u in uri]) - self.passwd[realm][reduced_uri] = (user, passwd) - - def find_user_password(self, realm, authuri): - domains = self.passwd.get(realm, {}) - for default_port in True, False: - reduced_authuri = self.reduce_uri(authuri, default_port) - for uris, authinfo in domains.items(): - for uri in uris: - if self.is_suburi(uri, reduced_authuri): - return authinfo - return None, None - - def reduce_uri(self, uri, default_port=True): - """Accept authority or URI and extract only the authority and path.""" - # note HTTP URLs do not have a userinfo component - parts = urlsplit(uri) - if parts[1]: - # URI - scheme = parts[0] - authority = parts[1] - path = parts[2] or '/' - else: - # host or host:port - scheme = None - authority = uri - path = '/' - host, port = splitport(authority) - if default_port and port is None and scheme is not None: - dport = {"http": 80, - "https": 443, - }.get(scheme) - if dport is not None: - authority = "%s:%d" % (host, dport) - return authority, path - - def is_suburi(self, base, test): - """Check if test is below base in a URI tree - - Both args must be URIs in reduced form. - """ - if base == test: - return True - if base[0] != test[0]: - return False - common = posixpath.commonprefix((base[1], test[1])) - if len(common) == len(base[1]): - return True - return False - - -class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr): - - def find_user_password(self, realm, authuri): - user, password = HTTPPasswordMgr.find_user_password(self, realm, - authuri) - if user is not None: - return user, password - return HTTPPasswordMgr.find_user_password(self, None, authuri) - - -class AbstractBasicAuthHandler(object): - - # XXX this allows for multiple auth-schemes, but will stupidly pick - # the last one with a realm specified. - - # allow for double- and single-quoted realm values - # (single quotes are a violation of the RFC, but appear in the wild) - rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' - 'realm=(["\']?)([^"\']*)\\2', re.I) - - # XXX could pre-emptively send auth info already accepted (RFC 2617, - # end of section 2, and section 1.2 immediately after "credentials" - # production). - - def __init__(self, password_mgr=None): - if password_mgr is None: - password_mgr = HTTPPasswordMgr() - self.passwd = password_mgr - self.add_password = self.passwd.add_password - self.retried = 0 - - def reset_retry_count(self): - self.retried = 0 - - def http_error_auth_reqed(self, authreq, host, req, headers): - # host may be an authority (without userinfo) or a URL with an - # authority - # XXX could be multiple headers - authreq = headers.get(authreq, None) - - if self.retried > 5: - # retry sending the username:password 5 times before failing. - raise HTTPError(req.get_full_url(), 401, "basic auth failed", - headers, None) - else: - self.retried += 1 - - if authreq: - scheme = authreq.split()[0] - if scheme.lower() != 'basic': - raise ValueError("AbstractBasicAuthHandler does not" - " support the following scheme: '%s'" % - scheme) - else: - mo = AbstractBasicAuthHandler.rx.search(authreq) - if mo: - scheme, quote, realm = mo.groups() - if quote not in ['"',"'"]: - warnings.warn("Basic Auth Realm was unquoted", - UserWarning, 2) - if scheme.lower() == 'basic': - response = self.retry_http_basic_auth(host, req, realm) - if response and response.code != 401: - self.retried = 0 - return response - - def retry_http_basic_auth(self, host, req, realm): - user, pw = self.passwd.find_user_password(realm, host) - if pw is not None: - raw = "%s:%s" % (user, pw) - auth = "Basic " + base64.b64encode(raw.encode()).decode("ascii") - if req.headers.get(self.auth_header, None) == auth: - return None - req.add_unredirected_header(self.auth_header, auth) - return self.parent.open(req, timeout=req.timeout) - else: - return None - - -class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler): - - auth_header = 'Authorization' - - def http_error_401(self, req, fp, code, msg, headers): - url = req.full_url - response = self.http_error_auth_reqed('www-authenticate', - url, req, headers) - self.reset_retry_count() - return response - - -class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler): - - auth_header = 'Proxy-authorization' - - def http_error_407(self, req, fp, code, msg, headers): - # http_error_auth_reqed requires that there is no userinfo component in - # authority. Assume there isn't one, since urllib.request does not (and - # should not, RFC 3986 s. 3.2.1) support requests for URLs containing - # userinfo. - authority = req.host - response = self.http_error_auth_reqed('proxy-authenticate', - authority, req, headers) - self.reset_retry_count() - return response - - -# Return n random bytes. -_randombytes = os.urandom - - -class AbstractDigestAuthHandler(object): - # Digest authentication is specified in RFC 2617. - - # XXX The client does not inspect the Authentication-Info header - # in a successful response. - - # XXX It should be possible to test this implementation against - # a mock server that just generates a static set of challenges. - - # XXX qop="auth-int" supports is shaky - - def __init__(self, passwd=None): - if passwd is None: - passwd = HTTPPasswordMgr() - self.passwd = passwd - self.add_password = self.passwd.add_password - self.retried = 0 - self.nonce_count = 0 - self.last_nonce = None - - def reset_retry_count(self): - self.retried = 0 - - def http_error_auth_reqed(self, auth_header, host, req, headers): - authreq = headers.get(auth_header, None) - if self.retried > 5: - # Don't fail endlessly - if we failed once, we'll probably - # fail a second time. Hm. Unless the Password Manager is - # prompting for the information. Crap. This isn't great - # but it's better than the current 'repeat until recursion - # depth exceeded' approach - raise HTTPError(req.full_url, 401, "digest auth failed", - headers, None) - else: - self.retried += 1 - if authreq: - scheme = authreq.split()[0] - if scheme.lower() == 'digest': - return self.retry_http_digest_auth(req, authreq) - elif scheme.lower() != 'basic': - raise ValueError("AbstractDigestAuthHandler does not support" - " the following scheme: '%s'" % scheme) - - def retry_http_digest_auth(self, req, auth): - token, challenge = auth.split(' ', 1) - chal = parse_keqv_list(filter(None, parse_http_list(challenge))) - auth = self.get_authorization(req, chal) - if auth: - auth_val = 'Digest %s' % auth - if req.headers.get(self.auth_header, None) == auth_val: - return None - req.add_unredirected_header(self.auth_header, auth_val) - resp = self.parent.open(req, timeout=req.timeout) - return resp - - def get_cnonce(self, nonce): - # The cnonce-value is an opaque - # quoted string value provided by the client and used by both client - # and server to avoid chosen plaintext attacks, to provide mutual - # authentication, and to provide some message integrity protection. - # This isn't a fabulous effort, but it's probably Good Enough. - s = "%s:%s:%s:" % (self.nonce_count, nonce, time.ctime()) - b = s.encode("ascii") + _randombytes(8) - dig = hashlib.sha1(b).hexdigest() - return dig[:16] - - def get_authorization(self, req, chal): - try: - realm = chal['realm'] - nonce = chal['nonce'] - qop = chal.get('qop') - algorithm = chal.get('algorithm', 'MD5') - # mod_digest doesn't send an opaque, even though it isn't - # supposed to be optional - opaque = chal.get('opaque', None) - except KeyError: - return None - - H, KD = self.get_algorithm_impls(algorithm) - if H is None: - return None - - user, pw = self.passwd.find_user_password(realm, req.full_url) - if user is None: - return None - - # XXX not implemented yet - if req.data is not None: - entdig = self.get_entity_digest(req.data, chal) - else: - entdig = None - - A1 = "%s:%s:%s" % (user, realm, pw) - A2 = "%s:%s" % (req.get_method(), - # XXX selector: what about proxies and full urls - req.selector) - if qop == 'auth': - if nonce == self.last_nonce: - self.nonce_count += 1 - else: - self.nonce_count = 1 - self.last_nonce = nonce - ncvalue = '%08x' % self.nonce_count - cnonce = self.get_cnonce(nonce) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2)) - respdig = KD(H(A1), noncebit) - elif qop is None: - respdig = KD(H(A1), "%s:%s" % (nonce, H(A2))) - else: - # XXX handle auth-int. - raise URLError("qop '%s' is not supported." % qop) - - # XXX should the partial digests be encoded too? - - base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ - 'response="%s"' % (user, realm, nonce, req.selector, - respdig) - if opaque: - base += ', opaque="%s"' % opaque - if entdig: - base += ', digest="%s"' % entdig - base += ', algorithm="%s"' % algorithm - if qop: - base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce) - return base - - def get_algorithm_impls(self, algorithm): - # lambdas assume digest modules are imported at the top level - if algorithm == 'MD5': - H = lambda x: hashlib.md5(x.encode("ascii")).hexdigest() - elif algorithm == 'SHA': - H = lambda x: hashlib.sha1(x.encode("ascii")).hexdigest() - # XXX MD5-sess - KD = lambda s, d: H("%s:%s" % (s, d)) - return H, KD - - def get_entity_digest(self, data, chal): - # XXX not implemented yet - return None - - -class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): - """An authentication protocol defined by RFC 2069 - - Digest authentication improves on basic authentication because it - does not transmit passwords in the clear. - """ - - auth_header = 'Authorization' - handler_order = 490 # before Basic auth - - def http_error_401(self, req, fp, code, msg, headers): - host = urlparse(req.full_url)[1] - retry = self.http_error_auth_reqed('www-authenticate', - host, req, headers) - self.reset_retry_count() - return retry - - -class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): - - auth_header = 'Proxy-Authorization' - handler_order = 490 # before Basic auth - - def http_error_407(self, req, fp, code, msg, headers): - host = req.host - retry = self.http_error_auth_reqed('proxy-authenticate', - host, req, headers) - self.reset_retry_count() - return retry - -class AbstractHTTPHandler(BaseHandler): - - def __init__(self, debuglevel=0): - self._debuglevel = debuglevel - - def set_http_debuglevel(self, level): - self._debuglevel = level - - def do_request_(self, request): - host = request.host - if not host: - raise URLError('no host given') - - if request.data is not None: # POST - data = request.data - if isinstance(data, str): - msg = "POST data should be bytes or an iterable of bytes. " \ - "It cannot be of type str." - raise TypeError(msg) - if not request.has_header('Content-type'): - request.add_unredirected_header( - 'Content-type', - 'application/x-www-form-urlencoded') - if not request.has_header('Content-length'): - size = None - try: - ### For Python-Future: - if PY2 and isinstance(data, array.array): - # memoryviews of arrays aren't supported - # in Py2.7. (e.g. memoryview(array.array('I', - # [1, 2, 3, 4])) raises a TypeError.) - # So we calculate the size manually instead: - size = len(data) * data.itemsize - ### - else: - mv = memoryview(data) - size = len(mv) * mv.itemsize - except TypeError: - if isinstance(data, collections.Iterable): - raise ValueError("Content-Length should be specified " - "for iterable data of type %r %r" % (type(data), - data)) - else: - request.add_unredirected_header( - 'Content-length', '%d' % size) - - sel_host = host - if request.has_proxy(): - scheme, sel = splittype(request.selector) - sel_host, sel_path = splithost(sel) - if not request.has_header('Host'): - request.add_unredirected_header('Host', sel_host) - for name, value in self.parent.addheaders: - name = name.capitalize() - if not request.has_header(name): - request.add_unredirected_header(name, value) - - return request - - def do_open(self, http_class, req, **http_conn_args): - """Return an HTTPResponse object for the request, using http_class. - - http_class must implement the HTTPConnection API from http.client. - """ - host = req.host - if not host: - raise URLError('no host given') - - # will parse host:port - h = http_class(host, timeout=req.timeout, **http_conn_args) - - headers = dict(req.unredirected_hdrs) - headers.update(dict((k, v) for k, v in req.headers.items() - if k not in headers)) - - # TODO(jhylton): Should this be redesigned to handle - # persistent connections? - - # We want to make an HTTP/1.1 request, but the addinfourl - # class isn't prepared to deal with a persistent connection. - # It will try to read all remaining data from the socket, - # which will block while the server waits for the next request. - # So make sure the connection gets closed after the (only) - # request. - headers["Connection"] = "close" - headers = dict((name.title(), val) for name, val in headers.items()) - - if req._tunnel_host: - tunnel_headers = {} - proxy_auth_hdr = "Proxy-Authorization" - if proxy_auth_hdr in headers: - tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr] - # Proxy-Authorization should not be sent to origin - # server. - del headers[proxy_auth_hdr] - h.set_tunnel(req._tunnel_host, headers=tunnel_headers) - - try: - h.request(req.get_method(), req.selector, req.data, headers) - except socket.error as err: # timeout error - h.close() - raise URLError(err) - else: - r = h.getresponse() - # If the server does not send us a 'Connection: close' header, - # HTTPConnection assumes the socket should be left open. Manually - # mark the socket to be closed when this response object goes away. - if h.sock: - h.sock.close() - h.sock = None - - - r.url = req.get_full_url() - # This line replaces the .msg attribute of the HTTPResponse - # with .headers, because urllib clients expect the response to - # have the reason in .msg. It would be good to mark this - # attribute is deprecated and get then to use info() or - # .headers. - r.msg = r.reason - return r - - -class HTTPHandler(AbstractHTTPHandler): - - def http_open(self, req): - return self.do_open(http_client.HTTPConnection, req) - - http_request = AbstractHTTPHandler.do_request_ - -if hasattr(http_client, 'HTTPSConnection'): - - class HTTPSHandler(AbstractHTTPHandler): - - def __init__(self, debuglevel=0, context=None, check_hostname=None): - AbstractHTTPHandler.__init__(self, debuglevel) - self._context = context - self._check_hostname = check_hostname - - def https_open(self, req): - return self.do_open(http_client.HTTPSConnection, req, - context=self._context, check_hostname=self._check_hostname) - - https_request = AbstractHTTPHandler.do_request_ - - __all__.append('HTTPSHandler') - -class HTTPCookieProcessor(BaseHandler): - def __init__(self, cookiejar=None): - import future.backports.http.cookiejar as http_cookiejar - if cookiejar is None: - cookiejar = http_cookiejar.CookieJar() - self.cookiejar = cookiejar - - def http_request(self, request): - self.cookiejar.add_cookie_header(request) - return request - - def http_response(self, request, response): - self.cookiejar.extract_cookies(response, request) - return response - - https_request = http_request - https_response = http_response - -class UnknownHandler(BaseHandler): - def unknown_open(self, req): - type = req.type - raise URLError('unknown url type: %s' % type) - -def parse_keqv_list(l): - """Parse list of key=value strings where keys are not duplicated.""" - parsed = {} - for elt in l: - k, v = elt.split('=', 1) - if v[0] == '"' and v[-1] == '"': - v = v[1:-1] - parsed[k] = v - return parsed - -def parse_http_list(s): - """Parse lists as described by RFC 2068 Section 2. - - In particular, parse comma-separated lists where the elements of - the list may include quoted-strings. A quoted-string could - contain a comma. A non-quoted string could have quotes in the - middle. Neither commas nor quotes count if they are escaped. - Only double-quotes count, not single-quotes. - """ - res = [] - part = '' - - escape = quote = False - for cur in s: - if escape: - part += cur - escape = False - continue - if quote: - if cur == '\\': - escape = True - continue - elif cur == '"': - quote = False - part += cur - continue - - if cur == ',': - res.append(part) - part = '' - continue - - if cur == '"': - quote = True - - part += cur - - # append last part - if part: - res.append(part) - - return [part.strip() for part in res] - -class FileHandler(BaseHandler): - # Use local file or FTP depending on form of URL - def file_open(self, req): - url = req.selector - if url[:2] == '//' and url[2:3] != '/' and (req.host and - req.host != 'localhost'): - if not req.host is self.get_names(): - raise URLError("file:// scheme is supported only on localhost") - else: - return self.open_local_file(req) - - # names for the localhost - names = None - def get_names(self): - if FileHandler.names is None: - try: - FileHandler.names = tuple( - socket.gethostbyname_ex('localhost')[2] + - socket.gethostbyname_ex(socket.gethostname())[2]) - except socket.gaierror: - FileHandler.names = (socket.gethostbyname('localhost'),) - return FileHandler.names - - # not entirely sure what the rules are here - def open_local_file(self, req): - import future.backports.email.utils as email_utils - import mimetypes - host = req.host - filename = req.selector - localfile = url2pathname(filename) - try: - stats = os.stat(localfile) - size = stats.st_size - modified = email_utils.formatdate(stats.st_mtime, usegmt=True) - mtype = mimetypes.guess_type(filename)[0] - headers = email.message_from_string( - 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % - (mtype or 'text/plain', size, modified)) - if host: - host, port = splitport(host) - if not host or \ - (not port and _safe_gethostbyname(host) in self.get_names()): - if host: - origurl = 'file://' + host + filename - else: - origurl = 'file://' + filename - return addinfourl(open(localfile, 'rb'), headers, origurl) - except OSError as exp: - # users shouldn't expect OSErrors coming from urlopen() - raise URLError(exp) - raise URLError('file not on local host') - -def _safe_gethostbyname(host): - try: - return socket.gethostbyname(host) - except socket.gaierror: - return None - -class FTPHandler(BaseHandler): - def ftp_open(self, req): - import ftplib - import mimetypes - host = req.host - if not host: - raise URLError('ftp error: no host given') - host, port = splitport(host) - if port is None: - port = ftplib.FTP_PORT - else: - port = int(port) - - # username/password handling - user, host = splituser(host) - if user: - user, passwd = splitpasswd(user) - else: - passwd = None - host = unquote(host) - user = user or '' - passwd = passwd or '' - - try: - host = socket.gethostbyname(host) - except socket.error as msg: - raise URLError(msg) - path, attrs = splitattr(req.selector) - dirs = path.split('/') - dirs = list(map(unquote, dirs)) - dirs, file = dirs[:-1], dirs[-1] - if dirs and not dirs[0]: - dirs = dirs[1:] - try: - fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout) - type = file and 'I' or 'D' - for attr in attrs: - attr, value = splitvalue(attr) - if attr.lower() == 'type' and \ - value in ('a', 'A', 'i', 'I', 'd', 'D'): - type = value.upper() - fp, retrlen = fw.retrfile(file, type) - headers = "" - mtype = mimetypes.guess_type(req.full_url)[0] - if mtype: - headers += "Content-type: %s\n" % mtype - if retrlen is not None and retrlen >= 0: - headers += "Content-length: %d\n" % retrlen - headers = email.message_from_string(headers) - return addinfourl(fp, headers, req.full_url) - except ftplib.all_errors as exp: - exc = URLError('ftp error: %r' % exp) - raise_with_traceback(exc) - - def connect_ftp(self, user, passwd, host, port, dirs, timeout): - return ftpwrapper(user, passwd, host, port, dirs, timeout, - persistent=False) - -class CacheFTPHandler(FTPHandler): - # XXX would be nice to have pluggable cache strategies - # XXX this stuff is definitely not thread safe - def __init__(self): - self.cache = {} - self.timeout = {} - self.soonest = 0 - self.delay = 60 - self.max_conns = 16 - - def setTimeout(self, t): - self.delay = t - - def setMaxConns(self, m): - self.max_conns = m - - def connect_ftp(self, user, passwd, host, port, dirs, timeout): - key = user, host, port, '/'.join(dirs), timeout - if key in self.cache: - self.timeout[key] = time.time() + self.delay - else: - self.cache[key] = ftpwrapper(user, passwd, host, port, - dirs, timeout) - self.timeout[key] = time.time() + self.delay - self.check_cache() - return self.cache[key] - - def check_cache(self): - # first check for old ones - t = time.time() - if self.soonest <= t: - for k, v in list(self.timeout.items()): - if v < t: - self.cache[k].close() - del self.cache[k] - del self.timeout[k] - self.soonest = min(list(self.timeout.values())) - - # then check the size - if len(self.cache) == self.max_conns: - for k, v in list(self.timeout.items()): - if v == self.soonest: - del self.cache[k] - del self.timeout[k] - break - self.soonest = min(list(self.timeout.values())) - - def clear_cache(self): - for conn in self.cache.values(): - conn.close() - self.cache.clear() - self.timeout.clear() - - -# Code move from the old urllib module - -MAXFTPCACHE = 10 # Trim the ftp cache beyond this size - -# Helper for non-unix systems -if os.name == 'nt': - from nturl2path import url2pathname, pathname2url -else: - def url2pathname(pathname): - """OS-specific conversion from a relative URL of the 'file' scheme - to a file system path; not recommended for general use.""" - return unquote(pathname) - - def pathname2url(pathname): - """OS-specific conversion from a file system path to a relative URL - of the 'file' scheme; not recommended for general use.""" - return quote(pathname) - -# This really consists of two pieces: -# (1) a class which handles opening of all sorts of URLs -# (plus assorted utilities etc.) -# (2) a set of functions for parsing URLs -# XXX Should these be separated out into different modules? - - -ftpcache = {} -class URLopener(object): - """Class to open URLs. - This is a class rather than just a subroutine because we may need - more than one set of global protocol-specific options. - Note -- this is a base class for those who don't want the - automatic handling of errors type 302 (relocated) and 401 - (authorization needed).""" - - __tempfiles = None - - version = "Python-urllib/%s" % __version__ - - # Constructor - def __init__(self, proxies=None, **x509): - msg = "%(class)s style of invoking requests is deprecated. " \ - "Use newer urlopen functions/methods" % {'class': self.__class__.__name__} - warnings.warn(msg, DeprecationWarning, stacklevel=3) - if proxies is None: - proxies = getproxies() - assert hasattr(proxies, 'keys'), "proxies must be a mapping" - self.proxies = proxies - self.key_file = x509.get('key_file') - self.cert_file = x509.get('cert_file') - self.addheaders = [('User-Agent', self.version)] - self.__tempfiles = [] - self.__unlink = os.unlink # See cleanup() - self.tempcache = None - # Undocumented feature: if you assign {} to tempcache, - # it is used to cache files retrieved with - # self.retrieve(). This is not enabled by default - # since it does not work for changing documents (and I - # haven't got the logic to check expiration headers - # yet). - self.ftpcache = ftpcache - # Undocumented feature: you can use a different - # ftp cache by assigning to the .ftpcache member; - # in case you want logically independent URL openers - # XXX This is not threadsafe. Bah. - - def __del__(self): - self.close() - - def close(self): - self.cleanup() - - def cleanup(self): - # This code sometimes runs when the rest of this module - # has already been deleted, so it can't use any globals - # or import anything. - if self.__tempfiles: - for file in self.__tempfiles: - try: - self.__unlink(file) - except OSError: - pass - del self.__tempfiles[:] - if self.tempcache: - self.tempcache.clear() - - def addheader(self, *args): - """Add a header to be used by the HTTP interface only - e.g. u.addheader('Accept', 'sound/basic')""" - self.addheaders.append(args) - - # External interface - def open(self, fullurl, data=None): - """Use URLopener().open(file) instead of open(file, 'r').""" - fullurl = unwrap(to_bytes(fullurl)) - fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]|") - if self.tempcache and fullurl in self.tempcache: - filename, headers = self.tempcache[fullurl] - fp = open(filename, 'rb') - return addinfourl(fp, headers, fullurl) - urltype, url = splittype(fullurl) - if not urltype: - urltype = 'file' - if urltype in self.proxies: - proxy = self.proxies[urltype] - urltype, proxyhost = splittype(proxy) - host, selector = splithost(proxyhost) - url = (host, fullurl) # Signal special case to open_*() - else: - proxy = None - name = 'open_' + urltype - self.type = urltype - name = name.replace('-', '_') - if not hasattr(self, name): - if proxy: - return self.open_unknown_proxy(proxy, fullurl, data) - else: - return self.open_unknown(fullurl, data) - try: - if data is None: - return getattr(self, name)(url) - else: - return getattr(self, name)(url, data) - except HTTPError: - raise - except socket.error as msg: - raise_with_traceback(IOError('socket error', msg)) - - def open_unknown(self, fullurl, data=None): - """Overridable interface to open unknown URL type.""" - type, url = splittype(fullurl) - raise IOError('url error', 'unknown url type', type) - - def open_unknown_proxy(self, proxy, fullurl, data=None): - """Overridable interface to open unknown URL type.""" - type, url = splittype(fullurl) - raise IOError('url error', 'invalid proxy for %s' % type, proxy) - - # External interface - def retrieve(self, url, filename=None, reporthook=None, data=None): - """retrieve(url) returns (filename, headers) for a local object - or (tempfilename, headers) for a remote object.""" - url = unwrap(to_bytes(url)) - if self.tempcache and url in self.tempcache: - return self.tempcache[url] - type, url1 = splittype(url) - if filename is None and (not type or type == 'file'): - try: - fp = self.open_local_file(url1) - hdrs = fp.info() - fp.close() - return url2pathname(splithost(url1)[1]), hdrs - except IOError as msg: - pass - fp = self.open(url, data) - try: - headers = fp.info() - if filename: - tfp = open(filename, 'wb') - else: - import tempfile - garbage, path = splittype(url) - garbage, path = splithost(path or "") - path, garbage = splitquery(path or "") - path, garbage = splitattr(path or "") - suffix = os.path.splitext(path)[1] - (fd, filename) = tempfile.mkstemp(suffix) - self.__tempfiles.append(filename) - tfp = os.fdopen(fd, 'wb') - try: - result = filename, headers - if self.tempcache is not None: - self.tempcache[url] = result - bs = 1024*8 - size = -1 - read = 0 - blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) - if reporthook: - reporthook(blocknum, bs, size) - while 1: - block = fp.read(bs) - if not block: - break - read += len(block) - tfp.write(block) - blocknum += 1 - if reporthook: - reporthook(blocknum, bs, size) - finally: - tfp.close() - finally: - fp.close() - - # raise exception if actual size does not match content-length header - if size >= 0 and read < size: - raise ContentTooShortError( - "retrieval incomplete: got only %i out of %i bytes" - % (read, size), result) - - return result - - # Each method named open_ knows how to open that type of URL - - def _open_generic_http(self, connection_factory, url, data): - """Make an HTTP connection using connection_class. - - This is an internal method that should be called from - open_http() or open_https(). - - Arguments: - - connection_factory should take a host name and return an - HTTPConnection instance. - - url is the url to retrieval or a host, relative-path pair. - - data is payload for a POST request or None. - """ - - user_passwd = None - proxy_passwd= None - if isinstance(url, str): - host, selector = splithost(url) - if host: - user_passwd, host = splituser(host) - host = unquote(host) - realhost = host - else: - host, selector = url - # check whether the proxy contains authorization information - proxy_passwd, host = splituser(host) - # now we proceed with the url we want to obtain - urltype, rest = splittype(selector) - url = rest - user_passwd = None - if urltype.lower() != 'http': - realhost = None - else: - realhost, rest = splithost(rest) - if realhost: - user_passwd, realhost = splituser(realhost) - if user_passwd: - selector = "%s://%s%s" % (urltype, realhost, rest) - if proxy_bypass(realhost): - host = realhost - - if not host: raise IOError('http error', 'no host given') - - if proxy_passwd: - proxy_passwd = unquote(proxy_passwd) - proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('ascii') - else: - proxy_auth = None - - if user_passwd: - user_passwd = unquote(user_passwd) - auth = base64.b64encode(user_passwd.encode()).decode('ascii') - else: - auth = None - http_conn = connection_factory(host) - headers = {} - if proxy_auth: - headers["Proxy-Authorization"] = "Basic %s" % proxy_auth - if auth: - headers["Authorization"] = "Basic %s" % auth - if realhost: - headers["Host"] = realhost - - # Add Connection:close as we don't support persistent connections yet. - # This helps in closing the socket and avoiding ResourceWarning - - headers["Connection"] = "close" - - for header, value in self.addheaders: - headers[header] = value - - if data is not None: - headers["Content-Type"] = "application/x-www-form-urlencoded" - http_conn.request("POST", selector, data, headers) - else: - http_conn.request("GET", selector, headers=headers) - - try: - response = http_conn.getresponse() - except http_client.BadStatusLine: - # something went wrong with the HTTP status line - raise URLError("http protocol error: bad status line") - - # According to RFC 2616, "2xx" code indicates that the client's - # request was successfully received, understood, and accepted. - if 200 <= response.status < 300: - return addinfourl(response, response.msg, "http:" + url, - response.status) - else: - return self.http_error( - url, response.fp, - response.status, response.reason, response.msg, data) - - def open_http(self, url, data=None): - """Use HTTP protocol.""" - return self._open_generic_http(http_client.HTTPConnection, url, data) - - def http_error(self, url, fp, errcode, errmsg, headers, data=None): - """Handle http errors. - - Derived class can override this, or provide specific handlers - named http_error_DDD where DDD is the 3-digit error code.""" - # First check if there's a specific handler for this error - name = 'http_error_%d' % errcode - if hasattr(self, name): - method = getattr(self, name) - if data is None: - result = method(url, fp, errcode, errmsg, headers) - else: - result = method(url, fp, errcode, errmsg, headers, data) - if result: return result - return self.http_error_default(url, fp, errcode, errmsg, headers) - - def http_error_default(self, url, fp, errcode, errmsg, headers): - """Default error handler: close the connection and raise IOError.""" - fp.close() - raise HTTPError(url, errcode, errmsg, headers, None) - - if _have_ssl: - def _https_connection(self, host): - return http_client.HTTPSConnection(host, - key_file=self.key_file, - cert_file=self.cert_file) - - def open_https(self, url, data=None): - """Use HTTPS protocol.""" - return self._open_generic_http(self._https_connection, url, data) - - def open_file(self, url): - """Use local file or FTP depending on form of URL.""" - if not isinstance(url, str): - raise URLError('file error: proxy support for file protocol currently not implemented') - if url[:2] == '//' and url[2:3] != '/' and url[2:12].lower() != 'localhost/': - raise ValueError("file:// scheme is supported only on localhost") - else: - return self.open_local_file(url) - - def open_local_file(self, url): - """Use local file.""" - import future.backports.email.utils as email_utils - import mimetypes - host, file = splithost(url) - localname = url2pathname(file) - try: - stats = os.stat(localname) - except OSError as e: - raise URLError(e.strerror, e.filename) - size = stats.st_size - modified = email_utils.formatdate(stats.st_mtime, usegmt=True) - mtype = mimetypes.guess_type(url)[0] - headers = email.message_from_string( - 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % - (mtype or 'text/plain', size, modified)) - if not host: - urlfile = file - if file[:1] == '/': - urlfile = 'file://' + file - return addinfourl(open(localname, 'rb'), headers, urlfile) - host, port = splitport(host) - if (not port - and socket.gethostbyname(host) in ((localhost(),) + thishost())): - urlfile = file - if file[:1] == '/': - urlfile = 'file://' + file - elif file[:2] == './': - raise ValueError("local file url may start with / or file:. Unknown url of type: %s" % url) - return addinfourl(open(localname, 'rb'), headers, urlfile) - raise URLError('local file error: not on local host') - - def open_ftp(self, url): - """Use FTP protocol.""" - if not isinstance(url, str): - raise URLError('ftp error: proxy support for ftp protocol currently not implemented') - import mimetypes - host, path = splithost(url) - if not host: raise URLError('ftp error: no host given') - host, port = splitport(host) - user, host = splituser(host) - if user: user, passwd = splitpasswd(user) - else: passwd = None - host = unquote(host) - user = unquote(user or '') - passwd = unquote(passwd or '') - host = socket.gethostbyname(host) - if not port: - import ftplib - port = ftplib.FTP_PORT - else: - port = int(port) - path, attrs = splitattr(path) - path = unquote(path) - dirs = path.split('/') - dirs, file = dirs[:-1], dirs[-1] - if dirs and not dirs[0]: dirs = dirs[1:] - if dirs and not dirs[0]: dirs[0] = '/' - key = user, host, port, '/'.join(dirs) - # XXX thread unsafe! - if len(self.ftpcache) > MAXFTPCACHE: - # Prune the cache, rather arbitrarily - for k in self.ftpcache.keys(): - if k != key: - v = self.ftpcache[k] - del self.ftpcache[k] - v.close() - try: - if key not in self.ftpcache: - self.ftpcache[key] = \ - ftpwrapper(user, passwd, host, port, dirs) - if not file: type = 'D' - else: type = 'I' - for attr in attrs: - attr, value = splitvalue(attr) - if attr.lower() == 'type' and \ - value in ('a', 'A', 'i', 'I', 'd', 'D'): - type = value.upper() - (fp, retrlen) = self.ftpcache[key].retrfile(file, type) - mtype = mimetypes.guess_type("ftp:" + url)[0] - headers = "" - if mtype: - headers += "Content-Type: %s\n" % mtype - if retrlen is not None and retrlen >= 0: - headers += "Content-Length: %d\n" % retrlen - headers = email.message_from_string(headers) - return addinfourl(fp, headers, "ftp:" + url) - except ftperrors() as exp: - raise_with_traceback(URLError('ftp error %r' % exp)) - - def open_data(self, url, data=None): - """Use "data" URL.""" - if not isinstance(url, str): - raise URLError('data error: proxy support for data protocol currently not implemented') - # ignore POSTed data - # - # syntax of data URLs: - # dataurl := "data:" [ mediatype ] [ ";base64" ] "," data - # mediatype := [ type "/" subtype ] *( ";" parameter ) - # data := *urlchar - # parameter := attribute "=" value - try: - [type, data] = url.split(',', 1) - except ValueError: - raise IOError('data error', 'bad data URL') - if not type: - type = 'text/plain;charset=US-ASCII' - semi = type.rfind(';') - if semi >= 0 and '=' not in type[semi:]: - encoding = type[semi+1:] - type = type[:semi] - else: - encoding = '' - msg = [] - msg.append('Date: %s'%time.strftime('%a, %d %b %Y %H:%M:%S GMT', - time.gmtime(time.time()))) - msg.append('Content-type: %s' % type) - if encoding == 'base64': - # XXX is this encoding/decoding ok? - data = base64.decodebytes(data.encode('ascii')).decode('latin-1') - else: - data = unquote(data) - msg.append('Content-Length: %d' % len(data)) - msg.append('') - msg.append(data) - msg = '\n'.join(msg) - headers = email.message_from_string(msg) - f = io.StringIO(msg) - #f.fileno = None # needed for addinfourl - return addinfourl(f, headers, url) - - -class FancyURLopener(URLopener): - """Derived class with handlers for errors we can handle (perhaps).""" - - def __init__(self, *args, **kwargs): - URLopener.__init__(self, *args, **kwargs) - self.auth_cache = {} - self.tries = 0 - self.maxtries = 10 - - def http_error_default(self, url, fp, errcode, errmsg, headers): - """Default error handling -- don't raise an exception.""" - return addinfourl(fp, headers, "http:" + url, errcode) - - def http_error_302(self, url, fp, errcode, errmsg, headers, data=None): - """Error 302 -- relocated (temporarily).""" - self.tries += 1 - if self.maxtries and self.tries >= self.maxtries: - if hasattr(self, "http_error_500"): - meth = self.http_error_500 - else: - meth = self.http_error_default - self.tries = 0 - return meth(url, fp, 500, - "Internal Server Error: Redirect Recursion", headers) - result = self.redirect_internal(url, fp, errcode, errmsg, headers, - data) - self.tries = 0 - return result - - def redirect_internal(self, url, fp, errcode, errmsg, headers, data): - if 'location' in headers: - newurl = headers['location'] - elif 'uri' in headers: - newurl = headers['uri'] - else: - return - fp.close() - - # In case the server sent a relative URL, join with original: - newurl = urljoin(self.type + ":" + url, newurl) - - urlparts = urlparse(newurl) - - # For security reasons, we don't allow redirection to anything other - # than http, https and ftp. - - # We are using newer HTTPError with older redirect_internal method - # This older method will get deprecated in 3.3 - - if urlparts.scheme not in ('http', 'https', 'ftp', ''): - raise HTTPError(newurl, errcode, - errmsg + - " Redirection to url '%s' is not allowed." % newurl, - headers, fp) - - return self.open(newurl) - - def http_error_301(self, url, fp, errcode, errmsg, headers, data=None): - """Error 301 -- also relocated (permanently).""" - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - - def http_error_303(self, url, fp, errcode, errmsg, headers, data=None): - """Error 303 -- also relocated (essentially identical to 302).""" - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - - def http_error_307(self, url, fp, errcode, errmsg, headers, data=None): - """Error 307 -- relocated, but turn POST into error.""" - if data is None: - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - else: - return self.http_error_default(url, fp, errcode, errmsg, headers) - - def http_error_401(self, url, fp, errcode, errmsg, headers, data=None, - retry=False): - """Error 401 -- authentication required. - This function supports Basic authentication only.""" - if 'www-authenticate' not in headers: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - stuff = headers['www-authenticate'] - match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff) - if not match: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - scheme, realm = match.groups() - if scheme.lower() != 'basic': - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - if not retry: - URLopener.http_error_default(self, url, fp, errcode, errmsg, - headers) - name = 'retry_' + self.type + '_basic_auth' - if data is None: - return getattr(self,name)(url, realm) - else: - return getattr(self,name)(url, realm, data) - - def http_error_407(self, url, fp, errcode, errmsg, headers, data=None, - retry=False): - """Error 407 -- proxy authentication required. - This function supports Basic authentication only.""" - if 'proxy-authenticate' not in headers: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - stuff = headers['proxy-authenticate'] - match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff) - if not match: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - scheme, realm = match.groups() - if scheme.lower() != 'basic': - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - if not retry: - URLopener.http_error_default(self, url, fp, errcode, errmsg, - headers) - name = 'retry_proxy_' + self.type + '_basic_auth' - if data is None: - return getattr(self,name)(url, realm) - else: - return getattr(self,name)(url, realm, data) - - def retry_proxy_http_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - newurl = 'http://' + host + selector - proxy = self.proxies['http'] - urltype, proxyhost = splittype(proxy) - proxyhost, proxyselector = splithost(proxyhost) - i = proxyhost.find('@') + 1 - proxyhost = proxyhost[i:] - user, passwd = self.get_user_passwd(proxyhost, realm, i) - if not (user or passwd): return None - proxyhost = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), proxyhost) - self.proxies['http'] = 'http://' + proxyhost + proxyselector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_proxy_https_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - newurl = 'https://' + host + selector - proxy = self.proxies['https'] - urltype, proxyhost = splittype(proxy) - proxyhost, proxyselector = splithost(proxyhost) - i = proxyhost.find('@') + 1 - proxyhost = proxyhost[i:] - user, passwd = self.get_user_passwd(proxyhost, realm, i) - if not (user or passwd): return None - proxyhost = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), proxyhost) - self.proxies['https'] = 'https://' + proxyhost + proxyselector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_http_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - i = host.find('@') + 1 - host = host[i:] - user, passwd = self.get_user_passwd(host, realm, i) - if not (user or passwd): return None - host = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), host) - newurl = 'http://' + host + selector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_https_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - i = host.find('@') + 1 - host = host[i:] - user, passwd = self.get_user_passwd(host, realm, i) - if not (user or passwd): return None - host = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), host) - newurl = 'https://' + host + selector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def get_user_passwd(self, host, realm, clear_cache=0): - key = realm + '@' + host.lower() - if key in self.auth_cache: - if clear_cache: - del self.auth_cache[key] - else: - return self.auth_cache[key] - user, passwd = self.prompt_user_passwd(host, realm) - if user or passwd: self.auth_cache[key] = (user, passwd) - return user, passwd - - def prompt_user_passwd(self, host, realm): - """Override this in a GUI environment!""" - import getpass - try: - user = input("Enter username for %s at %s: " % (realm, host)) - passwd = getpass.getpass("Enter password for %s in %s at %s: " % - (user, realm, host)) - return user, passwd - except KeyboardInterrupt: - print() - return None, None - - -# Utility functions - -_localhost = None -def localhost(): - """Return the IP address of the magic hostname 'localhost'.""" - global _localhost - if _localhost is None: - _localhost = socket.gethostbyname('localhost') - return _localhost - -_thishost = None -def thishost(): - """Return the IP addresses of the current host.""" - global _thishost - if _thishost is None: - try: - _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2]) - except socket.gaierror: - _thishost = tuple(socket.gethostbyname_ex('localhost')[2]) - return _thishost - -_ftperrors = None -def ftperrors(): - """Return the set of errors raised by the FTP class.""" - global _ftperrors - if _ftperrors is None: - import ftplib - _ftperrors = ftplib.all_errors - return _ftperrors - -_noheaders = None -def noheaders(): - """Return an empty email Message object.""" - global _noheaders - if _noheaders is None: - _noheaders = email.message_from_string("") - return _noheaders - - -# Utility classes - -class ftpwrapper(object): - """Class used by open_ftp() for cache of open FTP connections.""" - - def __init__(self, user, passwd, host, port, dirs, timeout=None, - persistent=True): - self.user = user - self.passwd = passwd - self.host = host - self.port = port - self.dirs = dirs - self.timeout = timeout - self.refcount = 0 - self.keepalive = persistent - self.init() - - def init(self): - import ftplib - self.busy = 0 - self.ftp = ftplib.FTP() - self.ftp.connect(self.host, self.port, self.timeout) - self.ftp.login(self.user, self.passwd) - _target = '/'.join(self.dirs) - self.ftp.cwd(_target) - - def retrfile(self, file, type): - import ftplib - self.endtransfer() - if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1 - else: cmd = 'TYPE ' + type; isdir = 0 - try: - self.ftp.voidcmd(cmd) - except ftplib.all_errors: - self.init() - self.ftp.voidcmd(cmd) - conn = None - if file and not isdir: - # Try to retrieve as a file - try: - cmd = 'RETR ' + file - conn, retrlen = self.ftp.ntransfercmd(cmd) - except ftplib.error_perm as reason: - if str(reason)[:3] != '550': - raise_with_traceback(URLError('ftp error: %r' % reason)) - if not conn: - # Set transfer mode to ASCII! - self.ftp.voidcmd('TYPE A') - # Try a directory listing. Verify that directory exists. - if file: - pwd = self.ftp.pwd() - try: - try: - self.ftp.cwd(file) - except ftplib.error_perm as reason: - ### Was: - # raise URLError('ftp error: %r' % reason) from reason - exc = URLError('ftp error: %r' % reason) - exc.__cause__ = reason - raise exc - finally: - self.ftp.cwd(pwd) - cmd = 'LIST ' + file - else: - cmd = 'LIST' - conn, retrlen = self.ftp.ntransfercmd(cmd) - self.busy = 1 - - ftpobj = addclosehook(conn.makefile('rb'), self.file_close) - self.refcount += 1 - conn.close() - # Pass back both a suitably decorated object and a retrieval length - return (ftpobj, retrlen) - - def endtransfer(self): - self.busy = 0 - - def close(self): - self.keepalive = False - if self.refcount <= 0: - self.real_close() - - def file_close(self): - self.endtransfer() - self.refcount -= 1 - if self.refcount <= 0 and not self.keepalive: - self.real_close() - - def real_close(self): - self.endtransfer() - try: - self.ftp.close() - except ftperrors(): - pass - -# Proxy handling -def getproxies_environment(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Scan the environment for variables named _proxy; - this seems to be the standard convention. If you need a - different way, you can pass a proxies dictionary to the - [Fancy]URLopener constructor. - - """ - proxies = {} - for name, value in os.environ.items(): - name = name.lower() - if value and name[-6:] == '_proxy': - proxies[name[:-6]] = value - return proxies - -def proxy_bypass_environment(host): - """Test if proxies should not be used for a particular host. - - Checks the environment for a variable named no_proxy, which should - be a list of DNS suffixes separated by commas, or '*' for all hosts. - """ - no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '') - # '*' is special case for always bypass - if no_proxy == '*': - return 1 - # strip port off host - hostonly, port = splitport(host) - # check if the host ends with any of the DNS suffixes - no_proxy_list = [proxy.strip() for proxy in no_proxy.split(',')] - for name in no_proxy_list: - if name and (hostonly.endswith(name) or host.endswith(name)): - return 1 - # otherwise, don't bypass - return 0 - - -# This code tests an OSX specific data structure but is testable on all -# platforms -def _proxy_bypass_macosx_sysconf(host, proxy_settings): - """ - Return True iff this host shouldn't be accessed using a proxy - - This function uses the MacOSX framework SystemConfiguration - to fetch the proxy information. - - proxy_settings come from _scproxy._get_proxy_settings or get mocked ie: - { 'exclude_simple': bool, - 'exceptions': ['foo.bar', '*.bar.com', '127.0.0.1', '10.1', '10.0/16'] - } - """ - from fnmatch import fnmatch - - hostonly, port = splitport(host) - - def ip2num(ipAddr): - parts = ipAddr.split('.') - parts = list(map(int, parts)) - if len(parts) != 4: - parts = (parts + [0, 0, 0, 0])[:4] - return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3] - - # Check for simple host names: - if '.' not in host: - if proxy_settings['exclude_simple']: - return True - - hostIP = None - - for value in proxy_settings.get('exceptions', ()): - # Items in the list are strings like these: *.local, 169.254/16 - if not value: continue - - m = re.match(r"(\d+(?:\.\d+)*)(/\d+)?", value) - if m is not None: - if hostIP is None: - try: - hostIP = socket.gethostbyname(hostonly) - hostIP = ip2num(hostIP) - except socket.error: - continue - - base = ip2num(m.group(1)) - mask = m.group(2) - if mask is None: - mask = 8 * (m.group(1).count('.') + 1) - else: - mask = int(mask[1:]) - mask = 32 - mask - - if (hostIP >> mask) == (base >> mask): - return True - - elif fnmatch(host, value): - return True - - return False - - -if sys.platform == 'darwin': - from _scproxy import _get_proxy_settings, _get_proxies - - def proxy_bypass_macosx_sysconf(host): - proxy_settings = _get_proxy_settings() - return _proxy_bypass_macosx_sysconf(host, proxy_settings) - - def getproxies_macosx_sysconf(): - """Return a dictionary of scheme -> proxy server URL mappings. - - This function uses the MacOSX framework SystemConfiguration - to fetch the proxy information. - """ - return _get_proxies() - - - - def proxy_bypass(host): - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_macosx_sysconf(host) - - def getproxies(): - return getproxies_environment() or getproxies_macosx_sysconf() - - -elif os.name == 'nt': - def getproxies_registry(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Win32 uses the registry to store proxies. - - """ - proxies = {} - try: - import winreg - except ImportError: - # Std module, so should be around - but you never know! - return proxies - try: - internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') - proxyEnable = winreg.QueryValueEx(internetSettings, - 'ProxyEnable')[0] - if proxyEnable: - # Returned as Unicode but problems if not converted to ASCII - proxyServer = str(winreg.QueryValueEx(internetSettings, - 'ProxyServer')[0]) - if '=' in proxyServer: - # Per-protocol settings - for p in proxyServer.split(';'): - protocol, address = p.split('=', 1) - # See if address has a type:// prefix - if not re.match('^([^/:]+)://', address): - address = '%s://%s' % (protocol, address) - proxies[protocol] = address - else: - # Use one setting for all protocols - if proxyServer[:5] == 'http:': - proxies['http'] = proxyServer - else: - proxies['http'] = 'http://%s' % proxyServer - proxies['https'] = 'https://%s' % proxyServer - proxies['ftp'] = 'ftp://%s' % proxyServer - internetSettings.Close() - except (WindowsError, ValueError, TypeError): - # Either registry key not found etc, or the value in an - # unexpected format. - # proxies already set up to be empty so nothing to do - pass - return proxies - - def getproxies(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Returns settings gathered from the environment, if specified, - or the registry. - - """ - return getproxies_environment() or getproxies_registry() - - def proxy_bypass_registry(host): - try: - import winreg - except ImportError: - # Std modules, so should be around - but you never know! - return 0 - try: - internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') - proxyEnable = winreg.QueryValueEx(internetSettings, - 'ProxyEnable')[0] - proxyOverride = str(winreg.QueryValueEx(internetSettings, - 'ProxyOverride')[0]) - # ^^^^ Returned as Unicode but problems if not converted to ASCII - except WindowsError: - return 0 - if not proxyEnable or not proxyOverride: - return 0 - # try to make a host list from name and IP address. - rawHost, port = splitport(host) - host = [rawHost] - try: - addr = socket.gethostbyname(rawHost) - if addr != rawHost: - host.append(addr) - except socket.error: - pass - try: - fqdn = socket.getfqdn(rawHost) - if fqdn != rawHost: - host.append(fqdn) - except socket.error: - pass - # make a check value list from the registry entry: replace the - # '' string by the localhost entry and the corresponding - # canonical entry. - proxyOverride = proxyOverride.split(';') - # now check if we match one of the registry values. - for test in proxyOverride: - if test == '': - if '.' not in rawHost: - return 1 - test = test.replace(".", r"\.") # mask dots - test = test.replace("*", r".*") # change glob sequence - test = test.replace("?", r".") # change glob char - for val in host: - if re.match(test, val, re.I): - return 1 - return 0 - - def proxy_bypass(host): - """Return a dictionary of scheme -> proxy server URL mappings. - - Returns settings gathered from the environment, if specified, - or the registry. - - """ - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_registry(host) - -else: - # By default use environment variables - getproxies = getproxies_environment - proxy_bypass = proxy_bypass_environment diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/response.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/response.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/response.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/response.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -"""Response classes used by urllib. - -The base class, addbase, defines a minimal file-like interface, -including read() and readline(). The typical response object is an -addinfourl instance, which defines an info() method that returns -headers and a geturl() method that returns the url. -""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import object - -class addbase(object): - """Base class for addinfo and addclosehook.""" - - # XXX Add a method to expose the timeout on the underlying socket? - - def __init__(self, fp): - # TODO(jhylton): Is there a better way to delegate using io? - self.fp = fp - self.read = self.fp.read - self.readline = self.fp.readline - # TODO(jhylton): Make sure an object with readlines() is also iterable - if hasattr(self.fp, "readlines"): - self.readlines = self.fp.readlines - if hasattr(self.fp, "fileno"): - self.fileno = self.fp.fileno - else: - self.fileno = lambda: None - - def __iter__(self): - # Assigning `__iter__` to the instance doesn't work as intended - # because the iter builtin does something like `cls.__iter__(obj)` - # and thus fails to find the _bound_ method `obj.__iter__`. - # Returning just `self.fp` works for built-in file objects but - # might not work for general file-like objects. - return iter(self.fp) - - def __repr__(self): - return '<%s at %r whose fp = %r>' % (self.__class__.__name__, - id(self), self.fp) - - def close(self): - if self.fp: - self.fp.close() - self.fp = None - self.read = None - self.readline = None - self.readlines = None - self.fileno = None - self.__iter__ = None - self.__next__ = None - - def __enter__(self): - if self.fp is None: - raise ValueError("I/O operation on closed file") - return self - - def __exit__(self, type, value, traceback): - self.close() - -class addclosehook(addbase): - """Class to add a close hook to an open file.""" - - def __init__(self, fp, closehook, *hookargs): - addbase.__init__(self, fp) - self.closehook = closehook - self.hookargs = hookargs - - def close(self): - if self.closehook: - self.closehook(*self.hookargs) - self.closehook = None - self.hookargs = None - addbase.close(self) - -class addinfo(addbase): - """class to add an info() method to an open file.""" - - def __init__(self, fp, headers): - addbase.__init__(self, fp) - self.headers = headers - - def info(self): - return self.headers - -class addinfourl(addbase): - """class to add info() and geturl() methods to an open file.""" - - def __init__(self, fp, headers, url, code=None): - addbase.__init__(self, fp) - self.headers = headers - self.url = url - self.code = code - - def info(self): - return self.headers - - def getcode(self): - return self.code - - def geturl(self): - return self.url - -del absolute_import, division, unicode_literals, object diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/robotparser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/robotparser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/urllib/robotparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/urllib/robotparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,211 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals -from future.builtins import str -""" robotparser.py - - Copyright (C) 2000 Bastian Kleineidam - - You can choose between two licenses when using this package: - 1) GNU GPLv2 - 2) PSF license for Python 2.2 - - The robots.txt Exclusion Protocol is implemented as specified in - http://info.webcrawler.com/mak/projects/robots/norobots-rfc.html -""" - -# Was: import urllib.parse, urllib.request -from future.backports import urllib -from future.backports.urllib import parse as _parse, request as _request -urllib.parse = _parse -urllib.request = _request - - -__all__ = ["RobotFileParser"] - -class RobotFileParser(object): - """ This class provides a set of methods to read, parse and answer - questions about a single robots.txt file. - - """ - - def __init__(self, url=''): - self.entries = [] - self.default_entry = None - self.disallow_all = False - self.allow_all = False - self.set_url(url) - self.last_checked = 0 - - def mtime(self): - """Returns the time the robots.txt file was last fetched. - - This is useful for long-running web spiders that need to - check for new robots.txt files periodically. - - """ - return self.last_checked - - def modified(self): - """Sets the time the robots.txt file was last fetched to the - current time. - - """ - import time - self.last_checked = time.time() - - def set_url(self, url): - """Sets the URL referring to a robots.txt file.""" - self.url = url - self.host, self.path = urllib.parse.urlparse(url)[1:3] - - def read(self): - """Reads the robots.txt URL and feeds it to the parser.""" - try: - f = urllib.request.urlopen(self.url) - except urllib.error.HTTPError as err: - if err.code in (401, 403): - self.disallow_all = True - elif err.code >= 400: - self.allow_all = True - else: - raw = f.read() - self.parse(raw.decode("utf-8").splitlines()) - - def _add_entry(self, entry): - if "*" in entry.useragents: - # the default entry is considered last - if self.default_entry is None: - # the first default entry wins - self.default_entry = entry - else: - self.entries.append(entry) - - def parse(self, lines): - """Parse the input lines from a robots.txt file. - - We allow that a user-agent: line is not preceded by - one or more blank lines. - """ - # states: - # 0: start state - # 1: saw user-agent line - # 2: saw an allow or disallow line - state = 0 - entry = Entry() - - for line in lines: - if not line: - if state == 1: - entry = Entry() - state = 0 - elif state == 2: - self._add_entry(entry) - entry = Entry() - state = 0 - # remove optional comment and strip line - i = line.find('#') - if i >= 0: - line = line[:i] - line = line.strip() - if not line: - continue - line = line.split(':', 1) - if len(line) == 2: - line[0] = line[0].strip().lower() - line[1] = urllib.parse.unquote(line[1].strip()) - if line[0] == "user-agent": - if state == 2: - self._add_entry(entry) - entry = Entry() - entry.useragents.append(line[1]) - state = 1 - elif line[0] == "disallow": - if state != 0: - entry.rulelines.append(RuleLine(line[1], False)) - state = 2 - elif line[0] == "allow": - if state != 0: - entry.rulelines.append(RuleLine(line[1], True)) - state = 2 - if state == 2: - self._add_entry(entry) - - - def can_fetch(self, useragent, url): - """using the parsed robots.txt decide if useragent can fetch url""" - if self.disallow_all: - return False - if self.allow_all: - return True - # search for given user agent matches - # the first match counts - parsed_url = urllib.parse.urlparse(urllib.parse.unquote(url)) - url = urllib.parse.urlunparse(('','',parsed_url.path, - parsed_url.params,parsed_url.query, parsed_url.fragment)) - url = urllib.parse.quote(url) - if not url: - url = "/" - for entry in self.entries: - if entry.applies_to(useragent): - return entry.allowance(url) - # try the default entry last - if self.default_entry: - return self.default_entry.allowance(url) - # agent not found ==> access granted - return True - - def __str__(self): - return ''.join([str(entry) + "\n" for entry in self.entries]) - - -class RuleLine(object): - """A rule line is a single "Allow:" (allowance==True) or "Disallow:" - (allowance==False) followed by a path.""" - def __init__(self, path, allowance): - if path == '' and not allowance: - # an empty value means allow all - allowance = True - self.path = urllib.parse.quote(path) - self.allowance = allowance - - def applies_to(self, filename): - return self.path == "*" or filename.startswith(self.path) - - def __str__(self): - return (self.allowance and "Allow" or "Disallow") + ": " + self.path - - -class Entry(object): - """An entry has one or more user-agents and zero or more rulelines""" - def __init__(self): - self.useragents = [] - self.rulelines = [] - - def __str__(self): - ret = [] - for agent in self.useragents: - ret.extend(["User-agent: ", agent, "\n"]) - for line in self.rulelines: - ret.extend([str(line), "\n"]) - return ''.join(ret) - - def applies_to(self, useragent): - """check if this entry applies to the specified agent""" - # split the name token and make it lower case - useragent = useragent.split("/")[0].lower() - for agent in self.useragents: - if agent == '*': - # we have the catch-all agent - return True - agent = agent.lower() - if agent in useragent: - return True - return False - - def allowance(self, filename): - """Preconditions: - - our agent applies to this entry - - filename is URL decoded""" - for line in self.rulelines: - if line.applies_to(filename): - return line.allowance - return True diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/client.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/client.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1496 +0,0 @@ -# -# XML-RPC CLIENT LIBRARY -# $Id$ -# -# an XML-RPC client interface for Python. -# -# the marshalling and response parser code can also be used to -# implement XML-RPC servers. -# -# Notes: -# this version is designed to work with Python 2.1 or newer. -# -# History: -# 1999-01-14 fl Created -# 1999-01-15 fl Changed dateTime to use localtime -# 1999-01-16 fl Added Binary/base64 element, default to RPC2 service -# 1999-01-19 fl Fixed array data element (from Skip Montanaro) -# 1999-01-21 fl Fixed dateTime constructor, etc. -# 1999-02-02 fl Added fault handling, handle empty sequences, etc. -# 1999-02-10 fl Fixed problem with empty responses (from Skip Montanaro) -# 1999-06-20 fl Speed improvements, pluggable parsers/transports (0.9.8) -# 2000-11-28 fl Changed boolean to check the truth value of its argument -# 2001-02-24 fl Added encoding/Unicode/SafeTransport patches -# 2001-02-26 fl Added compare support to wrappers (0.9.9/1.0b1) -# 2001-03-28 fl Make sure response tuple is a singleton -# 2001-03-29 fl Don't require empty params element (from Nicholas Riley) -# 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2) -# 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod) -# 2001-09-03 fl Allow Transport subclass to override getparser -# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup) -# 2001-10-01 fl Remove containers from memo cache when done with them -# 2001-10-01 fl Use faster escape method (80% dumps speedup) -# 2001-10-02 fl More dumps microtuning -# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum) -# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow -# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems) -# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix) -# 2002-03-17 fl Avoid buffered read when possible (from James Rucker) -# 2002-04-07 fl Added pythondoc comments -# 2002-04-16 fl Added __str__ methods to datetime/binary wrappers -# 2002-05-15 fl Added error constants (from Andrew Kuchling) -# 2002-06-27 fl Merged with Python CVS version -# 2002-10-22 fl Added basic authentication (based on code from Phillip Eby) -# 2003-01-22 sm Add support for the bool type -# 2003-02-27 gvr Remove apply calls -# 2003-04-24 sm Use cStringIO if available -# 2003-04-25 ak Add support for nil -# 2003-06-15 gn Add support for time.struct_time -# 2003-07-12 gp Correct marshalling of Faults -# 2003-10-31 mvl Add multicall support -# 2004-08-20 mvl Bump minimum supported Python version to 2.1 -# -# Copyright (c) 1999-2002 by Secret Labs AB. -# Copyright (c) 1999-2002 by Fredrik Lundh. -# -# info@pythonware.com -# http://www.pythonware.com -# -# -------------------------------------------------------------------- -# The XML-RPC client interface is -# -# Copyright (c) 1999-2002 by Secret Labs AB -# Copyright (c) 1999-2002 by Fredrik Lundh -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: -# -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Secret Labs AB or the author not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD -# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- -# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR -# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# -------------------------------------------------------------------- - -""" -Ported using Python-Future from the Python 3.3 standard library. - -An XML-RPC client interface for Python. - -The marshalling and response parser code can also be used to -implement XML-RPC servers. - -Exported exceptions: - - Error Base class for client errors - ProtocolError Indicates an HTTP protocol error - ResponseError Indicates a broken response package - Fault Indicates an XML-RPC fault package - -Exported classes: - - ServerProxy Represents a logical connection to an XML-RPC server - - MultiCall Executor of boxcared xmlrpc requests - DateTime dateTime wrapper for an ISO 8601 string or time tuple or - localtime integer value to generate a "dateTime.iso8601" - XML-RPC value - Binary binary data wrapper - - Marshaller Generate an XML-RPC params chunk from a Python data structure - Unmarshaller Unmarshal an XML-RPC response from incoming XML event message - Transport Handles an HTTP transaction to an XML-RPC server - SafeTransport Handles an HTTPS transaction to an XML-RPC server - -Exported constants: - - (none) - -Exported functions: - - getparser Create instance of the fastest available parser & attach - to an unmarshalling object - dumps Convert an argument tuple or a Fault instance to an XML-RPC - request (or response, if the methodresponse option is used). - loads Convert an XML-RPC packet to unmarshalled data plus a method - name (None if not present). -""" - -from __future__ import (absolute_import, division, print_function, - unicode_literals) -from future.builtins import bytes, dict, int, range, str - -import base64 -# Py2.7 compatibility hack -base64.encodebytes = base64.encodestring -base64.decodebytes = base64.decodestring -import sys -import time -from datetime import datetime -from future.backports.http import client as http_client -from future.backports.urllib import parse as urllib_parse -from future.utils import ensure_new_type -from xml.parsers import expat -import socket -import errno -from io import BytesIO -try: - import gzip -except ImportError: - gzip = None #python can be built without zlib/gzip support - -# -------------------------------------------------------------------- -# Internal stuff - -def escape(s): - s = s.replace("&", "&") - s = s.replace("<", "<") - return s.replace(">", ">",) - -# used in User-Agent header sent -__version__ = sys.version[:3] - -# xmlrpc integer limits -MAXINT = 2**31-1 -MININT = -2**31 - -# -------------------------------------------------------------------- -# Error constants (from Dan Libby's specification at -# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php) - -# Ranges of errors -PARSE_ERROR = -32700 -SERVER_ERROR = -32600 -APPLICATION_ERROR = -32500 -SYSTEM_ERROR = -32400 -TRANSPORT_ERROR = -32300 - -# Specific errors -NOT_WELLFORMED_ERROR = -32700 -UNSUPPORTED_ENCODING = -32701 -INVALID_ENCODING_CHAR = -32702 -INVALID_XMLRPC = -32600 -METHOD_NOT_FOUND = -32601 -INVALID_METHOD_PARAMS = -32602 -INTERNAL_ERROR = -32603 - -# -------------------------------------------------------------------- -# Exceptions - -## -# Base class for all kinds of client-side errors. - -class Error(Exception): - """Base class for client errors.""" - def __str__(self): - return repr(self) - -## -# Indicates an HTTP-level protocol error. This is raised by the HTTP -# transport layer, if the server returns an error code other than 200 -# (OK). -# -# @param url The target URL. -# @param errcode The HTTP error code. -# @param errmsg The HTTP error message. -# @param headers The HTTP header dictionary. - -class ProtocolError(Error): - """Indicates an HTTP protocol error.""" - def __init__(self, url, errcode, errmsg, headers): - Error.__init__(self) - self.url = url - self.errcode = errcode - self.errmsg = errmsg - self.headers = headers - def __repr__(self): - return ( - "" % - (self.url, self.errcode, self.errmsg) - ) - -## -# Indicates a broken XML-RPC response package. This exception is -# raised by the unmarshalling layer, if the XML-RPC response is -# malformed. - -class ResponseError(Error): - """Indicates a broken response package.""" - pass - -## -# Indicates an XML-RPC fault response package. This exception is -# raised by the unmarshalling layer, if the XML-RPC response contains -# a fault string. This exception can also be used as a class, to -# generate a fault XML-RPC message. -# -# @param faultCode The XML-RPC fault code. -# @param faultString The XML-RPC fault string. - -class Fault(Error): - """Indicates an XML-RPC fault package.""" - def __init__(self, faultCode, faultString, **extra): - Error.__init__(self) - self.faultCode = faultCode - self.faultString = faultString - def __repr__(self): - return "" % (ensure_new_type(self.faultCode), - ensure_new_type(self.faultString)) - -# -------------------------------------------------------------------- -# Special values - -## -# Backwards compatibility - -boolean = Boolean = bool - -## -# Wrapper for XML-RPC DateTime values. This converts a time value to -# the format used by XML-RPC. -#

-# The value can be given as a datetime object, as a string in the -# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by -# time.localtime()), or an integer value (as returned by time.time()). -# The wrapper uses time.localtime() to convert an integer to a time -# tuple. -# -# @param value The time, given as a datetime object, an ISO 8601 string, -# a time tuple, or an integer time value. - - -### For Python-Future: -def _iso8601_format(value): - return "%04d%02d%02dT%02d:%02d:%02d" % ( - value.year, value.month, value.day, - value.hour, value.minute, value.second) -### -# Issue #13305: different format codes across platforms -# _day0 = datetime(1, 1, 1) -# if _day0.strftime('%Y') == '0001': # Mac OS X -# def _iso8601_format(value): -# return value.strftime("%Y%m%dT%H:%M:%S") -# elif _day0.strftime('%4Y') == '0001': # Linux -# def _iso8601_format(value): -# return value.strftime("%4Y%m%dT%H:%M:%S") -# else: -# def _iso8601_format(value): -# return value.strftime("%Y%m%dT%H:%M:%S").zfill(17) -# del _day0 - - -def _strftime(value): - if isinstance(value, datetime): - return _iso8601_format(value) - - if not isinstance(value, (tuple, time.struct_time)): - if value == 0: - value = time.time() - value = time.localtime(value) - - return "%04d%02d%02dT%02d:%02d:%02d" % value[:6] - -class DateTime(object): - """DateTime wrapper for an ISO 8601 string or time tuple or - localtime integer value to generate 'dateTime.iso8601' XML-RPC - value. - """ - - def __init__(self, value=0): - if isinstance(value, str): - self.value = value - else: - self.value = _strftime(value) - - def make_comparable(self, other): - if isinstance(other, DateTime): - s = self.value - o = other.value - elif isinstance(other, datetime): - s = self.value - o = _iso8601_format(other) - elif isinstance(other, str): - s = self.value - o = other - elif hasattr(other, "timetuple"): - s = self.timetuple() - o = other.timetuple() - else: - otype = (hasattr(other, "__class__") - and other.__class__.__name__ - or type(other)) - raise TypeError("Can't compare %s and %s" % - (self.__class__.__name__, otype)) - return s, o - - def __lt__(self, other): - s, o = self.make_comparable(other) - return s < o - - def __le__(self, other): - s, o = self.make_comparable(other) - return s <= o - - def __gt__(self, other): - s, o = self.make_comparable(other) - return s > o - - def __ge__(self, other): - s, o = self.make_comparable(other) - return s >= o - - def __eq__(self, other): - s, o = self.make_comparable(other) - return s == o - - def __ne__(self, other): - s, o = self.make_comparable(other) - return s != o - - def timetuple(self): - return time.strptime(self.value, "%Y%m%dT%H:%M:%S") - - ## - # Get date/time value. - # - # @return Date/time value, as an ISO 8601 string. - - def __str__(self): - return self.value - - def __repr__(self): - return "" % (ensure_new_type(self.value), id(self)) - - def decode(self, data): - self.value = str(data).strip() - - def encode(self, out): - out.write("") - out.write(self.value) - out.write("\n") - -def _datetime(data): - # decode xml element contents into a DateTime structure. - value = DateTime() - value.decode(data) - return value - -def _datetime_type(data): - return datetime.strptime(data, "%Y%m%dT%H:%M:%S") - -## -# Wrapper for binary data. This can be used to transport any kind -# of binary data over XML-RPC, using BASE64 encoding. -# -# @param data An 8-bit string containing arbitrary data. - -class Binary(object): - """Wrapper for binary data.""" - - def __init__(self, data=None): - if data is None: - data = b"" - else: - if not isinstance(data, (bytes, bytearray)): - raise TypeError("expected bytes or bytearray, not %s" % - data.__class__.__name__) - data = bytes(data) # Make a copy of the bytes! - self.data = data - - ## - # Get buffer contents. - # - # @return Buffer contents, as an 8-bit string. - - def __str__(self): - return str(self.data, "latin-1") # XXX encoding?! - - def __eq__(self, other): - if isinstance(other, Binary): - other = other.data - return self.data == other - - def __ne__(self, other): - if isinstance(other, Binary): - other = other.data - return self.data != other - - def decode(self, data): - self.data = base64.decodebytes(data) - - def encode(self, out): - out.write("\n") - encoded = base64.encodebytes(self.data) - out.write(encoded.decode('ascii')) - out.write("\n") - -def _binary(data): - # decode xml element contents into a Binary structure - value = Binary() - value.decode(data) - return value - -WRAPPERS = (DateTime, Binary) - -# -------------------------------------------------------------------- -# XML parsers - -class ExpatParser(object): - # fast expat parser for Python 2.0 and later. - def __init__(self, target): - self._parser = parser = expat.ParserCreate(None, None) - self._target = target - parser.StartElementHandler = target.start - parser.EndElementHandler = target.end - parser.CharacterDataHandler = target.data - encoding = None - target.xml(encoding, None) - - def feed(self, data): - self._parser.Parse(data, 0) - - def close(self): - self._parser.Parse("", 1) # end of data - del self._target, self._parser # get rid of circular references - -# -------------------------------------------------------------------- -# XML-RPC marshalling and unmarshalling code - -## -# XML-RPC marshaller. -# -# @param encoding Default encoding for 8-bit strings. The default -# value is None (interpreted as UTF-8). -# @see dumps - -class Marshaller(object): - """Generate an XML-RPC params chunk from a Python data structure. - - Create a Marshaller instance for each set of parameters, and use - the "dumps" method to convert your data (represented as a tuple) - to an XML-RPC params chunk. To write a fault response, pass a - Fault instance instead. You may prefer to use the "dumps" module - function for this purpose. - """ - - # by the way, if you don't understand what's going on in here, - # that's perfectly ok. - - def __init__(self, encoding=None, allow_none=False): - self.memo = {} - self.data = None - self.encoding = encoding - self.allow_none = allow_none - - dispatch = {} - - def dumps(self, values): - out = [] - write = out.append - dump = self.__dump - if isinstance(values, Fault): - # fault instance - write("\n") - dump({'faultCode': values.faultCode, - 'faultString': values.faultString}, - write) - write("\n") - else: - # parameter block - # FIXME: the xml-rpc specification allows us to leave out - # the entire block if there are no parameters. - # however, changing this may break older code (including - # old versions of xmlrpclib.py), so this is better left as - # is for now. See @XMLRPC3 for more information. /F - write("\n") - for v in values: - write("\n") - dump(v, write) - write("\n") - write("\n") - result = "".join(out) - return str(result) - - def __dump(self, value, write): - try: - f = self.dispatch[type(ensure_new_type(value))] - except KeyError: - # check if this object can be marshalled as a structure - if not hasattr(value, '__dict__'): - raise TypeError("cannot marshal %s objects" % type(value)) - # check if this class is a sub-class of a basic type, - # because we don't know how to marshal these types - # (e.g. a string sub-class) - for type_ in type(value).__mro__: - if type_ in self.dispatch.keys(): - raise TypeError("cannot marshal %s objects" % type(value)) - # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix - # for the p3yk merge, this should probably be fixed more neatly. - f = self.dispatch["_arbitrary_instance"] - f(self, value, write) - - def dump_nil (self, value, write): - if not self.allow_none: - raise TypeError("cannot marshal None unless allow_none is enabled") - write("") - dispatch[type(None)] = dump_nil - - def dump_bool(self, value, write): - write("") - write(value and "1" or "0") - write("\n") - dispatch[bool] = dump_bool - - def dump_long(self, value, write): - if value > MAXINT or value < MININT: - raise OverflowError("long int exceeds XML-RPC limits") - write("") - write(str(int(value))) - write("\n") - dispatch[int] = dump_long - - # backward compatible - dump_int = dump_long - - def dump_double(self, value, write): - write("") - write(repr(ensure_new_type(value))) - write("\n") - dispatch[float] = dump_double - - def dump_unicode(self, value, write, escape=escape): - write("") - write(escape(value)) - write("\n") - dispatch[str] = dump_unicode - - def dump_bytes(self, value, write): - write("\n") - encoded = base64.encodebytes(value) - write(encoded.decode('ascii')) - write("\n") - dispatch[bytes] = dump_bytes - dispatch[bytearray] = dump_bytes - - def dump_array(self, value, write): - i = id(value) - if i in self.memo: - raise TypeError("cannot marshal recursive sequences") - self.memo[i] = None - dump = self.__dump - write("\n") - for v in value: - dump(v, write) - write("\n") - del self.memo[i] - dispatch[tuple] = dump_array - dispatch[list] = dump_array - - def dump_struct(self, value, write, escape=escape): - i = id(value) - if i in self.memo: - raise TypeError("cannot marshal recursive dictionaries") - self.memo[i] = None - dump = self.__dump - write("\n") - for k, v in value.items(): - write("\n") - if not isinstance(k, str): - raise TypeError("dictionary key must be string") - write("%s\n" % escape(k)) - dump(v, write) - write("\n") - write("\n") - del self.memo[i] - dispatch[dict] = dump_struct - - def dump_datetime(self, value, write): - write("") - write(_strftime(value)) - write("\n") - dispatch[datetime] = dump_datetime - - def dump_instance(self, value, write): - # check for special wrappers - if value.__class__ in WRAPPERS: - self.write = write - value.encode(self) - del self.write - else: - # store instance attributes as a struct (really?) - self.dump_struct(value.__dict__, write) - dispatch[DateTime] = dump_instance - dispatch[Binary] = dump_instance - # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix - # for the p3yk merge, this should probably be fixed more neatly. - dispatch["_arbitrary_instance"] = dump_instance - -## -# XML-RPC unmarshaller. -# -# @see loads - -class Unmarshaller(object): - """Unmarshal an XML-RPC response, based on incoming XML event - messages (start, data, end). Call close() to get the resulting - data structure. - - Note that this reader is fairly tolerant, and gladly accepts bogus - XML-RPC data without complaining (but not bogus XML). - """ - - # and again, if you don't understand what's going on in here, - # that's perfectly ok. - - def __init__(self, use_datetime=False, use_builtin_types=False): - self._type = None - self._stack = [] - self._marks = [] - self._data = [] - self._methodname = None - self._encoding = "utf-8" - self.append = self._stack.append - self._use_datetime = use_builtin_types or use_datetime - self._use_bytes = use_builtin_types - - def close(self): - # return response tuple and target method - if self._type is None or self._marks: - raise ResponseError() - if self._type == "fault": - raise Fault(**self._stack[0]) - return tuple(self._stack) - - def getmethodname(self): - return self._methodname - - # - # event handlers - - def xml(self, encoding, standalone): - self._encoding = encoding - # FIXME: assert standalone == 1 ??? - - def start(self, tag, attrs): - # prepare to handle this element - if tag == "array" or tag == "struct": - self._marks.append(len(self._stack)) - self._data = [] - self._value = (tag == "value") - - def data(self, text): - self._data.append(text) - - def end(self, tag): - # call the appropriate end tag handler - try: - f = self.dispatch[tag] - except KeyError: - pass # unknown tag ? - else: - return f(self, "".join(self._data)) - - # - # accelerator support - - def end_dispatch(self, tag, data): - # dispatch data - try: - f = self.dispatch[tag] - except KeyError: - pass # unknown tag ? - else: - return f(self, data) - - # - # element decoders - - dispatch = {} - - def end_nil (self, data): - self.append(None) - self._value = 0 - dispatch["nil"] = end_nil - - def end_boolean(self, data): - if data == "0": - self.append(False) - elif data == "1": - self.append(True) - else: - raise TypeError("bad boolean value") - self._value = 0 - dispatch["boolean"] = end_boolean - - def end_int(self, data): - self.append(int(data)) - self._value = 0 - dispatch["i4"] = end_int - dispatch["i8"] = end_int - dispatch["int"] = end_int - - def end_double(self, data): - self.append(float(data)) - self._value = 0 - dispatch["double"] = end_double - - def end_string(self, data): - if self._encoding: - data = data.decode(self._encoding) - self.append(data) - self._value = 0 - dispatch["string"] = end_string - dispatch["name"] = end_string # struct keys are always strings - - def end_array(self, data): - mark = self._marks.pop() - # map arrays to Python lists - self._stack[mark:] = [self._stack[mark:]] - self._value = 0 - dispatch["array"] = end_array - - def end_struct(self, data): - mark = self._marks.pop() - # map structs to Python dictionaries - dict = {} - items = self._stack[mark:] - for i in range(0, len(items), 2): - dict[items[i]] = items[i+1] - self._stack[mark:] = [dict] - self._value = 0 - dispatch["struct"] = end_struct - - def end_base64(self, data): - value = Binary() - value.decode(data.encode("ascii")) - if self._use_bytes: - value = value.data - self.append(value) - self._value = 0 - dispatch["base64"] = end_base64 - - def end_dateTime(self, data): - value = DateTime() - value.decode(data) - if self._use_datetime: - value = _datetime_type(data) - self.append(value) - dispatch["dateTime.iso8601"] = end_dateTime - - def end_value(self, data): - # if we stumble upon a value element with no internal - # elements, treat it as a string element - if self._value: - self.end_string(data) - dispatch["value"] = end_value - - def end_params(self, data): - self._type = "params" - dispatch["params"] = end_params - - def end_fault(self, data): - self._type = "fault" - dispatch["fault"] = end_fault - - def end_methodName(self, data): - if self._encoding: - data = data.decode(self._encoding) - self._methodname = data - self._type = "methodName" # no params - dispatch["methodName"] = end_methodName - -## Multicall support -# - -class _MultiCallMethod(object): - # some lesser magic to store calls made to a MultiCall object - # for batch execution - def __init__(self, call_list, name): - self.__call_list = call_list - self.__name = name - def __getattr__(self, name): - return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name)) - def __call__(self, *args): - self.__call_list.append((self.__name, args)) - -class MultiCallIterator(object): - """Iterates over the results of a multicall. Exceptions are - raised in response to xmlrpc faults.""" - - def __init__(self, results): - self.results = results - - def __getitem__(self, i): - item = self.results[i] - if isinstance(type(item), dict): - raise Fault(item['faultCode'], item['faultString']) - elif type(item) == type([]): - return item[0] - else: - raise ValueError("unexpected type in multicall result") - -class MultiCall(object): - """server -> a object used to boxcar method calls - - server should be a ServerProxy object. - - Methods can be added to the MultiCall using normal - method call syntax e.g.: - - multicall = MultiCall(server_proxy) - multicall.add(2,3) - multicall.get_address("Guido") - - To execute the multicall, call the MultiCall object e.g.: - - add_result, address = multicall() - """ - - def __init__(self, server): - self.__server = server - self.__call_list = [] - - def __repr__(self): - return "" % id(self) - - __str__ = __repr__ - - def __getattr__(self, name): - return _MultiCallMethod(self.__call_list, name) - - def __call__(self): - marshalled_list = [] - for name, args in self.__call_list: - marshalled_list.append({'methodName' : name, 'params' : args}) - - return MultiCallIterator(self.__server.system.multicall(marshalled_list)) - -# -------------------------------------------------------------------- -# convenience functions - -FastMarshaller = FastParser = FastUnmarshaller = None - -## -# Create a parser object, and connect it to an unmarshalling instance. -# This function picks the fastest available XML parser. -# -# return A (parser, unmarshaller) tuple. - -def getparser(use_datetime=False, use_builtin_types=False): - """getparser() -> parser, unmarshaller - - Create an instance of the fastest available parser, and attach it - to an unmarshalling object. Return both objects. - """ - if FastParser and FastUnmarshaller: - if use_builtin_types: - mkdatetime = _datetime_type - mkbytes = base64.decodebytes - elif use_datetime: - mkdatetime = _datetime_type - mkbytes = _binary - else: - mkdatetime = _datetime - mkbytes = _binary - target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault) - parser = FastParser(target) - else: - target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types) - if FastParser: - parser = FastParser(target) - else: - parser = ExpatParser(target) - return parser, target - -## -# Convert a Python tuple or a Fault instance to an XML-RPC packet. -# -# @def dumps(params, **options) -# @param params A tuple or Fault instance. -# @keyparam methodname If given, create a methodCall request for -# this method name. -# @keyparam methodresponse If given, create a methodResponse packet. -# If used with a tuple, the tuple must be a singleton (that is, -# it must contain exactly one element). -# @keyparam encoding The packet encoding. -# @return A string containing marshalled data. - -def dumps(params, methodname=None, methodresponse=None, encoding=None, - allow_none=False): - """data [,options] -> marshalled data - - Convert an argument tuple or a Fault instance to an XML-RPC - request (or response, if the methodresponse option is used). - - In addition to the data object, the following options can be given - as keyword arguments: - - methodname: the method name for a methodCall packet - - methodresponse: true to create a methodResponse packet. - If this option is used with a tuple, the tuple must be - a singleton (i.e. it can contain only one element). - - encoding: the packet encoding (default is UTF-8) - - All byte strings in the data structure are assumed to use the - packet encoding. Unicode strings are automatically converted, - where necessary. - """ - - assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance" - if isinstance(params, Fault): - methodresponse = 1 - elif methodresponse and isinstance(params, tuple): - assert len(params) == 1, "response tuple must be a singleton" - - if not encoding: - encoding = "utf-8" - - if FastMarshaller: - m = FastMarshaller(encoding) - else: - m = Marshaller(encoding, allow_none) - - data = m.dumps(params) - - if encoding != "utf-8": - xmlheader = "\n" % str(encoding) - else: - xmlheader = "\n" # utf-8 is default - - # standard XML-RPC wrappings - if methodname: - # a method call - if not isinstance(methodname, str): - methodname = methodname.encode(encoding) - data = ( - xmlheader, - "\n" - "", methodname, "\n", - data, - "\n" - ) - elif methodresponse: - # a method response, or a fault structure - data = ( - xmlheader, - "\n", - data, - "\n" - ) - else: - return data # return as is - return str("").join(data) - -## -# Convert an XML-RPC packet to a Python object. If the XML-RPC packet -# represents a fault condition, this function raises a Fault exception. -# -# @param data An XML-RPC packet, given as an 8-bit string. -# @return A tuple containing the unpacked data, and the method name -# (None if not present). -# @see Fault - -def loads(data, use_datetime=False, use_builtin_types=False): - """data -> unmarshalled data, method name - - Convert an XML-RPC packet to unmarshalled data plus a method - name (None if not present). - - If the XML-RPC packet represents a fault condition, this function - raises a Fault exception. - """ - p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types) - p.feed(data) - p.close() - return u.close(), u.getmethodname() - -## -# Encode a string using the gzip content encoding such as specified by the -# Content-Encoding: gzip -# in the HTTP header, as described in RFC 1952 -# -# @param data the unencoded data -# @return the encoded data - -def gzip_encode(data): - """data -> gzip encoded data - - Encode data using the gzip content encoding as described in RFC 1952 - """ - if not gzip: - raise NotImplementedError - f = BytesIO() - gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) - gzf.write(data) - gzf.close() - encoded = f.getvalue() - f.close() - return encoded - -## -# Decode a string using the gzip content encoding such as specified by the -# Content-Encoding: gzip -# in the HTTP header, as described in RFC 1952 -# -# @param data The encoded data -# @return the unencoded data -# @raises ValueError if data is not correctly coded. - -def gzip_decode(data): - """gzip encoded data -> unencoded data - - Decode data using the gzip content encoding as described in RFC 1952 - """ - if not gzip: - raise NotImplementedError - f = BytesIO(data) - gzf = gzip.GzipFile(mode="rb", fileobj=f) - try: - decoded = gzf.read() - except IOError: - raise ValueError("invalid data") - f.close() - gzf.close() - return decoded - -## -# Return a decoded file-like object for the gzip encoding -# as described in RFC 1952. -# -# @param response A stream supporting a read() method -# @return a file-like object that the decoded data can be read() from - -class GzipDecodedResponse(gzip.GzipFile if gzip else object): - """a file-like object to decode a response encoded with the gzip - method, as described in RFC 1952. - """ - def __init__(self, response): - #response doesn't support tell() and read(), required by - #GzipFile - if not gzip: - raise NotImplementedError - self.io = BytesIO(response.read()) - gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io) - - def close(self): - gzip.GzipFile.close(self) - self.io.close() - - -# -------------------------------------------------------------------- -# request dispatcher - -class _Method(object): - # some magic to bind an XML-RPC method to an RPC server. - # supports "nested" methods (e.g. examples.getStateName) - def __init__(self, send, name): - self.__send = send - self.__name = name - def __getattr__(self, name): - return _Method(self.__send, "%s.%s" % (self.__name, name)) - def __call__(self, *args): - return self.__send(self.__name, args) - -## -# Standard transport class for XML-RPC over HTTP. -#

-# You can create custom transports by subclassing this method, and -# overriding selected methods. - -class Transport(object): - """Handles an HTTP transaction to an XML-RPC server.""" - - # client identifier (may be overridden) - user_agent = "Python-xmlrpc/%s" % __version__ - - #if true, we'll request gzip encoding - accept_gzip_encoding = True - - # if positive, encode request using gzip if it exceeds this threshold - # note that many server will get confused, so only use it if you know - # that they can decode such a request - encode_threshold = None #None = don't encode - - def __init__(self, use_datetime=False, use_builtin_types=False): - self._use_datetime = use_datetime - self._use_builtin_types = use_builtin_types - self._connection = (None, None) - self._extra_headers = [] - - ## - # Send a complete request, and parse the response. - # Retry request if a cached connection has disconnected. - # - # @param host Target host. - # @param handler Target PRC handler. - # @param request_body XML-RPC request body. - # @param verbose Debugging flag. - # @return Parsed response. - - def request(self, host, handler, request_body, verbose=False): - #retry request once if cached connection has gone cold - for i in (0, 1): - try: - return self.single_request(host, handler, request_body, verbose) - except socket.error as e: - if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE): - raise - except http_client.BadStatusLine: #close after we sent request - if i: - raise - - def single_request(self, host, handler, request_body, verbose=False): - # issue XML-RPC request - try: - http_conn = self.send_request(host, handler, request_body, verbose) - resp = http_conn.getresponse() - if resp.status == 200: - self.verbose = verbose - return self.parse_response(resp) - - except Fault: - raise - except Exception: - #All unexpected errors leave connection in - # a strange state, so we clear it. - self.close() - raise - - #We got an error response. - #Discard any response data and raise exception - if resp.getheader("content-length", ""): - resp.read() - raise ProtocolError( - host + handler, - resp.status, resp.reason, - dict(resp.getheaders()) - ) - - - ## - # Create parser. - # - # @return A 2-tuple containing a parser and a unmarshaller. - - def getparser(self): - # get parser and unmarshaller - return getparser(use_datetime=self._use_datetime, - use_builtin_types=self._use_builtin_types) - - ## - # Get authorization info from host parameter - # Host may be a string, or a (host, x509-dict) tuple; if a string, - # it is checked for a "user:pw@host" format, and a "Basic - # Authentication" header is added if appropriate. - # - # @param host Host descriptor (URL or (URL, x509 info) tuple). - # @return A 3-tuple containing (actual host, extra headers, - # x509 info). The header and x509 fields may be None. - - def get_host_info(self, host): - - x509 = {} - if isinstance(host, tuple): - host, x509 = host - - auth, host = urllib_parse.splituser(host) - - if auth: - auth = urllib_parse.unquote_to_bytes(auth) - auth = base64.encodebytes(auth).decode("utf-8") - auth = "".join(auth.split()) # get rid of whitespace - extra_headers = [ - ("Authorization", "Basic " + auth) - ] - else: - extra_headers = [] - - return host, extra_headers, x509 - - ## - # Connect to server. - # - # @param host Target host. - # @return An HTTPConnection object - - def make_connection(self, host): - #return an existing connection if possible. This allows - #HTTP/1.1 keep-alive. - if self._connection and host == self._connection[0]: - return self._connection[1] - # create a HTTP connection object from a host descriptor - chost, self._extra_headers, x509 = self.get_host_info(host) - self._connection = host, http_client.HTTPConnection(chost) - return self._connection[1] - - ## - # Clear any cached connection object. - # Used in the event of socket errors. - # - def close(self): - if self._connection[1]: - self._connection[1].close() - self._connection = (None, None) - - ## - # Send HTTP request. - # - # @param host Host descriptor (URL or (URL, x509 info) tuple). - # @param handler Targer RPC handler (a path relative to host) - # @param request_body The XML-RPC request body - # @param debug Enable debugging if debug is true. - # @return An HTTPConnection. - - def send_request(self, host, handler, request_body, debug): - connection = self.make_connection(host) - headers = self._extra_headers[:] - if debug: - connection.set_debuglevel(1) - if self.accept_gzip_encoding and gzip: - connection.putrequest("POST", handler, skip_accept_encoding=True) - headers.append(("Accept-Encoding", "gzip")) - else: - connection.putrequest("POST", handler) - headers.append(("Content-Type", "text/xml")) - headers.append(("User-Agent", self.user_agent)) - self.send_headers(connection, headers) - self.send_content(connection, request_body) - return connection - - ## - # Send request headers. - # This function provides a useful hook for subclassing - # - # @param connection httpConnection. - # @param headers list of key,value pairs for HTTP headers - - def send_headers(self, connection, headers): - for key, val in headers: - connection.putheader(key, val) - - ## - # Send request body. - # This function provides a useful hook for subclassing - # - # @param connection httpConnection. - # @param request_body XML-RPC request body. - - def send_content(self, connection, request_body): - #optionally encode the request - if (self.encode_threshold is not None and - self.encode_threshold < len(request_body) and - gzip): - connection.putheader("Content-Encoding", "gzip") - request_body = gzip_encode(request_body) - - connection.putheader("Content-Length", str(len(request_body))) - connection.endheaders(request_body) - - ## - # Parse response. - # - # @param file Stream. - # @return Response tuple and target method. - - def parse_response(self, response): - # read response data from httpresponse, and parse it - # Check for new http response object, otherwise it is a file object. - if hasattr(response, 'getheader'): - if response.getheader("Content-Encoding", "") == "gzip": - stream = GzipDecodedResponse(response) - else: - stream = response - else: - stream = response - - p, u = self.getparser() - - while 1: - data = stream.read(1024) - if not data: - break - if self.verbose: - print("body:", repr(data)) - p.feed(data) - - if stream is not response: - stream.close() - p.close() - - return u.close() - -## -# Standard transport class for XML-RPC over HTTPS. - -class SafeTransport(Transport): - """Handles an HTTPS transaction to an XML-RPC server.""" - - # FIXME: mostly untested - - def make_connection(self, host): - if self._connection and host == self._connection[0]: - return self._connection[1] - - if not hasattr(http_client, "HTTPSConnection"): - raise NotImplementedError( - "your version of http.client doesn't support HTTPS") - # create a HTTPS connection object from a host descriptor - # host may be a string, or a (host, x509-dict) tuple - chost, self._extra_headers, x509 = self.get_host_info(host) - self._connection = host, http_client.HTTPSConnection(chost, - None, **(x509 or {})) - return self._connection[1] - -## -# Standard server proxy. This class establishes a virtual connection -# to an XML-RPC server. -#

-# This class is available as ServerProxy and Server. New code should -# use ServerProxy, to avoid confusion. -# -# @def ServerProxy(uri, **options) -# @param uri The connection point on the server. -# @keyparam transport A transport factory, compatible with the -# standard transport class. -# @keyparam encoding The default encoding used for 8-bit strings -# (default is UTF-8). -# @keyparam verbose Use a true value to enable debugging output. -# (printed to standard output). -# @see Transport - -class ServerProxy(object): - """uri [,options] -> a logical connection to an XML-RPC server - - uri is the connection point on the server, given as - scheme://host/target. - - The standard implementation always supports the "http" scheme. If - SSL socket support is available (Python 2.0), it also supports - "https". - - If the target part and the slash preceding it are both omitted, - "/RPC2" is assumed. - - The following options can be given as keyword arguments: - - transport: a transport factory - encoding: the request encoding (default is UTF-8) - - All 8-bit strings passed to the server proxy are assumed to use - the given encoding. - """ - - def __init__(self, uri, transport=None, encoding=None, verbose=False, - allow_none=False, use_datetime=False, use_builtin_types=False): - # establish a "logical" server connection - - # get the url - type, uri = urllib_parse.splittype(uri) - if type not in ("http", "https"): - raise IOError("unsupported XML-RPC protocol") - self.__host, self.__handler = urllib_parse.splithost(uri) - if not self.__handler: - self.__handler = "/RPC2" - - if transport is None: - if type == "https": - handler = SafeTransport - else: - handler = Transport - transport = handler(use_datetime=use_datetime, - use_builtin_types=use_builtin_types) - self.__transport = transport - - self.__encoding = encoding or 'utf-8' - self.__verbose = verbose - self.__allow_none = allow_none - - def __close(self): - self.__transport.close() - - def __request(self, methodname, params): - # call a method on the remote server - - request = dumps(params, methodname, encoding=self.__encoding, - allow_none=self.__allow_none).encode(self.__encoding) - - response = self.__transport.request( - self.__host, - self.__handler, - request, - verbose=self.__verbose - ) - - if len(response) == 1: - response = response[0] - - return response - - def __repr__(self): - return ( - "" % - (self.__host, self.__handler) - ) - - __str__ = __repr__ - - def __getattr__(self, name): - # magic method dispatcher - return _Method(self.__request, name) - - # note: to call a remote object with an non-standard name, use - # result getattr(server, "strange-python-name")(args) - - def __call__(self, attr): - """A workaround to get special attributes on the ServerProxy - without interfering with the magic __getattr__ - """ - if attr == "close": - return self.__close - elif attr == "transport": - return self.__transport - raise AttributeError("Attribute %r not found" % (attr,)) - -# compatibility - -Server = ServerProxy - -# -------------------------------------------------------------------- -# test code - -if __name__ == "__main__": - - # simple test program (from the XML-RPC specification) - - # local server, available from Lib/xmlrpc/server.py - server = ServerProxy("http://localhost:8000") - - try: - print(server.currentTime.getCurrentTime()) - except Error as v: - print("ERROR", v) - - multi = MultiCall(server) - multi.getData() - multi.pow(2,9) - multi.add(1,2) - try: - for response in multi(): - print(response) - except Error as v: - print("ERROR", v) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -# This directory is a Python package. diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/server.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/server.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/backports/xmlrpc/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,999 +0,0 @@ -r""" -Ported using Python-Future from the Python 3.3 standard library. - -XML-RPC Servers. - -This module can be used to create simple XML-RPC servers -by creating a server and either installing functions, a -class instance, or by extending the SimpleXMLRPCServer -class. - -It can also be used to handle XML-RPC requests in a CGI -environment using CGIXMLRPCRequestHandler. - -The Doc* classes can be used to create XML-RPC servers that -serve pydoc-style documentation in response to HTTP -GET requests. This documentation is dynamically generated -based on the functions and methods registered with the -server. - -A list of possible usage patterns follows: - -1. Install functions: - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_function(pow) -server.register_function(lambda x,y: x+y, 'add') -server.serve_forever() - -2. Install an instance: - -class MyFuncs: - def __init__(self): - # make all of the sys functions available through sys.func_name - import sys - self.sys = sys - def _listMethods(self): - # implement this method so that system.listMethods - # knows to advertise the sys methods - return list_public_methods(self) + \ - ['sys.' + method for method in list_public_methods(self.sys)] - def pow(self, x, y): return pow(x, y) - def add(self, x, y) : return x + y - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_introspection_functions() -server.register_instance(MyFuncs()) -server.serve_forever() - -3. Install an instance with custom dispatch method: - -class Math: - def _listMethods(self): - # this method must be present for system.listMethods - # to work - return ['add', 'pow'] - def _methodHelp(self, method): - # this method must be present for system.methodHelp - # to work - if method == 'add': - return "add(2,3) => 5" - elif method == 'pow': - return "pow(x, y[, z]) => number" - else: - # By convention, return empty - # string if no help is available - return "" - def _dispatch(self, method, params): - if method == 'pow': - return pow(*params) - elif method == 'add': - return params[0] + params[1] - else: - raise ValueError('bad method') - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_introspection_functions() -server.register_instance(Math()) -server.serve_forever() - -4. Subclass SimpleXMLRPCServer: - -class MathServer(SimpleXMLRPCServer): - def _dispatch(self, method, params): - try: - # We are forcing the 'export_' prefix on methods that are - # callable through XML-RPC to prevent potential security - # problems - func = getattr(self, 'export_' + method) - except AttributeError: - raise Exception('method "%s" is not supported' % method) - else: - return func(*params) - - def export_add(self, x, y): - return x + y - -server = MathServer(("localhost", 8000)) -server.serve_forever() - -5. CGI script: - -server = CGIXMLRPCRequestHandler() -server.register_function(pow) -server.handle_request() -""" - -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import int, str - -# Written by Brian Quinlan (brian@sweetapp.com). -# Based on code written by Fredrik Lundh. - -from future.backports.xmlrpc.client import Fault, dumps, loads, gzip_encode, gzip_decode -from future.backports.http.server import BaseHTTPRequestHandler -import future.backports.http.server as http_server -from future.backports import socketserver -import sys -import os -import re -import pydoc -import inspect -import traceback -try: - import fcntl -except ImportError: - fcntl = None - -def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): - """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d - - Resolves a dotted attribute name to an object. Raises - an AttributeError if any attribute in the chain starts with a '_'. - - If the optional allow_dotted_names argument is false, dots are not - supported and this function operates similar to getattr(obj, attr). - """ - - if allow_dotted_names: - attrs = attr.split('.') - else: - attrs = [attr] - - for i in attrs: - if i.startswith('_'): - raise AttributeError( - 'attempt to access private attribute "%s"' % i - ) - else: - obj = getattr(obj,i) - return obj - -def list_public_methods(obj): - """Returns a list of attribute strings, found in the specified - object, which represent callable attributes""" - - return [member for member in dir(obj) - if not member.startswith('_') and - callable(getattr(obj, member))] - -class SimpleXMLRPCDispatcher(object): - """Mix-in class that dispatches XML-RPC requests. - - This class is used to register XML-RPC method handlers - and then to dispatch them. This class doesn't need to be - instanced directly when used by SimpleXMLRPCServer but it - can be instanced when used by the MultiPathXMLRPCServer - """ - - def __init__(self, allow_none=False, encoding=None, - use_builtin_types=False): - self.funcs = {} - self.instance = None - self.allow_none = allow_none - self.encoding = encoding or 'utf-8' - self.use_builtin_types = use_builtin_types - - def register_instance(self, instance, allow_dotted_names=False): - """Registers an instance to respond to XML-RPC requests. - - Only one instance can be installed at a time. - - If the registered instance has a _dispatch method then that - method will be called with the name of the XML-RPC method and - its parameters as a tuple - e.g. instance._dispatch('add',(2,3)) - - If the registered instance does not have a _dispatch method - then the instance will be searched to find a matching method - and, if found, will be called. Methods beginning with an '_' - are considered private and will not be called by - SimpleXMLRPCServer. - - If a registered function matches a XML-RPC request, then it - will be called instead of the registered instance. - - If the optional allow_dotted_names argument is true and the - instance does not have a _dispatch method, method names - containing dots are supported and resolved, as long as none of - the name segments start with an '_'. - - *** SECURITY WARNING: *** - - Enabling the allow_dotted_names options allows intruders - to access your module's global variables and may allow - intruders to execute arbitrary code on your machine. Only - use this option on a secure, closed network. - - """ - - self.instance = instance - self.allow_dotted_names = allow_dotted_names - - def register_function(self, function, name=None): - """Registers a function to respond to XML-RPC requests. - - The optional name argument can be used to set a Unicode name - for the function. - """ - - if name is None: - name = function.__name__ - self.funcs[name] = function - - def register_introspection_functions(self): - """Registers the XML-RPC introspection methods in the system - namespace. - - see http://xmlrpc.usefulinc.com/doc/reserved.html - """ - - self.funcs.update({'system.listMethods' : self.system_listMethods, - 'system.methodSignature' : self.system_methodSignature, - 'system.methodHelp' : self.system_methodHelp}) - - def register_multicall_functions(self): - """Registers the XML-RPC multicall method in the system - namespace. - - see http://www.xmlrpc.com/discuss/msgReader$1208""" - - self.funcs.update({'system.multicall' : self.system_multicall}) - - def _marshaled_dispatch(self, data, dispatch_method = None, path = None): - """Dispatches an XML-RPC method from marshalled (XML) data. - - XML-RPC methods are dispatched from the marshalled (XML) data - using the _dispatch method and the result is returned as - marshalled data. For backwards compatibility, a dispatch - function can be provided as an argument (see comment in - SimpleXMLRPCRequestHandler.do_POST) but overriding the - existing method through subclassing is the preferred means - of changing method dispatch behavior. - """ - - try: - params, method = loads(data, use_builtin_types=self.use_builtin_types) - - # generate response - if dispatch_method is not None: - response = dispatch_method(method, params) - else: - response = self._dispatch(method, params) - # wrap response in a singleton tuple - response = (response,) - response = dumps(response, methodresponse=1, - allow_none=self.allow_none, encoding=self.encoding) - except Fault as fault: - response = dumps(fault, allow_none=self.allow_none, - encoding=self.encoding) - except: - # report exception back to server - exc_type, exc_value, exc_tb = sys.exc_info() - response = dumps( - Fault(1, "%s:%s" % (exc_type, exc_value)), - encoding=self.encoding, allow_none=self.allow_none, - ) - - return response.encode(self.encoding) - - def system_listMethods(self): - """system.listMethods() => ['add', 'subtract', 'multiple'] - - Returns a list of the methods supported by the server.""" - - methods = set(self.funcs.keys()) - if self.instance is not None: - # Instance can implement _listMethod to return a list of - # methods - if hasattr(self.instance, '_listMethods'): - methods |= set(self.instance._listMethods()) - # if the instance has a _dispatch method then we - # don't have enough information to provide a list - # of methods - elif not hasattr(self.instance, '_dispatch'): - methods |= set(list_public_methods(self.instance)) - return sorted(methods) - - def system_methodSignature(self, method_name): - """system.methodSignature('add') => [double, int, int] - - Returns a list describing the signature of the method. In the - above example, the add method takes two integers as arguments - and returns a double result. - - This server does NOT support system.methodSignature.""" - - # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html - - return 'signatures not supported' - - def system_methodHelp(self, method_name): - """system.methodHelp('add') => "Adds two integers together" - - Returns a string containing documentation for the specified method.""" - - method = None - if method_name in self.funcs: - method = self.funcs[method_name] - elif self.instance is not None: - # Instance can implement _methodHelp to return help for a method - if hasattr(self.instance, '_methodHelp'): - return self.instance._methodHelp(method_name) - # if the instance has a _dispatch method then we - # don't have enough information to provide help - elif not hasattr(self.instance, '_dispatch'): - try: - method = resolve_dotted_attribute( - self.instance, - method_name, - self.allow_dotted_names - ) - except AttributeError: - pass - - # Note that we aren't checking that the method actually - # be a callable object of some kind - if method is None: - return "" - else: - return pydoc.getdoc(method) - - def system_multicall(self, call_list): - """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \ -[[4], ...] - - Allows the caller to package multiple XML-RPC calls into a single - request. - - See http://www.xmlrpc.com/discuss/msgReader$1208 - """ - - results = [] - for call in call_list: - method_name = call['methodName'] - params = call['params'] - - try: - # XXX A marshalling error in any response will fail the entire - # multicall. If someone cares they should fix this. - results.append([self._dispatch(method_name, params)]) - except Fault as fault: - results.append( - {'faultCode' : fault.faultCode, - 'faultString' : fault.faultString} - ) - except: - exc_type, exc_value, exc_tb = sys.exc_info() - results.append( - {'faultCode' : 1, - 'faultString' : "%s:%s" % (exc_type, exc_value)} - ) - return results - - def _dispatch(self, method, params): - """Dispatches the XML-RPC method. - - XML-RPC calls are forwarded to a registered function that - matches the called XML-RPC method name. If no such function - exists then the call is forwarded to the registered instance, - if available. - - If the registered instance has a _dispatch method then that - method will be called with the name of the XML-RPC method and - its parameters as a tuple - e.g. instance._dispatch('add',(2,3)) - - If the registered instance does not have a _dispatch method - then the instance will be searched to find a matching method - and, if found, will be called. - - Methods beginning with an '_' are considered private and will - not be called. - """ - - func = None - try: - # check to see if a matching function has been registered - func = self.funcs[method] - except KeyError: - if self.instance is not None: - # check for a _dispatch method - if hasattr(self.instance, '_dispatch'): - return self.instance._dispatch(method, params) - else: - # call instance method directly - try: - func = resolve_dotted_attribute( - self.instance, - method, - self.allow_dotted_names - ) - except AttributeError: - pass - - if func is not None: - return func(*params) - else: - raise Exception('method "%s" is not supported' % method) - -class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler): - """Simple XML-RPC request handler class. - - Handles all HTTP POST requests and attempts to decode them as - XML-RPC requests. - """ - - # Class attribute listing the accessible path components; - # paths not on this list will result in a 404 error. - rpc_paths = ('/', '/RPC2') - - #if not None, encode responses larger than this, if possible - encode_threshold = 1400 #a common MTU - - #Override form StreamRequestHandler: full buffering of output - #and no Nagle. - wbufsize = -1 - disable_nagle_algorithm = True - - # a re to match a gzip Accept-Encoding - aepattern = re.compile(r""" - \s* ([^\s;]+) \s* #content-coding - (;\s* q \s*=\s* ([0-9\.]+))? #q - """, re.VERBOSE | re.IGNORECASE) - - def accept_encodings(self): - r = {} - ae = self.headers.get("Accept-Encoding", "") - for e in ae.split(","): - match = self.aepattern.match(e) - if match: - v = match.group(3) - v = float(v) if v else 1.0 - r[match.group(1)] = v - return r - - def is_rpc_path_valid(self): - if self.rpc_paths: - return self.path in self.rpc_paths - else: - # If .rpc_paths is empty, just assume all paths are legal - return True - - def do_POST(self): - """Handles the HTTP POST request. - - Attempts to interpret all HTTP POST requests as XML-RPC calls, - which are forwarded to the server's _dispatch method for handling. - """ - - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - try: - # Get arguments by reading body of request. - # We read this in chunks to avoid straining - # socket.read(); around the 10 or 15Mb mark, some platforms - # begin to have problems (bug #792570). - max_chunk_size = 10*1024*1024 - size_remaining = int(self.headers["content-length"]) - L = [] - while size_remaining: - chunk_size = min(size_remaining, max_chunk_size) - chunk = self.rfile.read(chunk_size) - if not chunk: - break - L.append(chunk) - size_remaining -= len(L[-1]) - data = b''.join(L) - - data = self.decode_request_content(data) - if data is None: - return #response has been sent - - # In previous versions of SimpleXMLRPCServer, _dispatch - # could be overridden in this class, instead of in - # SimpleXMLRPCDispatcher. To maintain backwards compatibility, - # check to see if a subclass implements _dispatch and dispatch - # using that method if present. - response = self.server._marshaled_dispatch( - data, getattr(self, '_dispatch', None), self.path - ) - except Exception as e: # This should only happen if the module is buggy - # internal error, report as HTTP server error - self.send_response(500) - - # Send information about the exception if requested - if hasattr(self.server, '_send_traceback_header') and \ - self.server._send_traceback_header: - self.send_header("X-exception", str(e)) - trace = traceback.format_exc() - trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII') - self.send_header("X-traceback", trace) - - self.send_header("Content-length", "0") - self.end_headers() - else: - self.send_response(200) - self.send_header("Content-type", "text/xml") - if self.encode_threshold is not None: - if len(response) > self.encode_threshold: - q = self.accept_encodings().get("gzip", 0) - if q: - try: - response = gzip_encode(response) - self.send_header("Content-Encoding", "gzip") - except NotImplementedError: - pass - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - - def decode_request_content(self, data): - #support gzip encoding of request - encoding = self.headers.get("content-encoding", "identity").lower() - if encoding == "identity": - return data - if encoding == "gzip": - try: - return gzip_decode(data) - except NotImplementedError: - self.send_response(501, "encoding %r not supported" % encoding) - except ValueError: - self.send_response(400, "error decoding gzip content") - else: - self.send_response(501, "encoding %r not supported" % encoding) - self.send_header("Content-length", "0") - self.end_headers() - - def report_404 (self): - # Report a 404 error - self.send_response(404) - response = b'No such page' - self.send_header("Content-type", "text/plain") - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - - def log_request(self, code='-', size='-'): - """Selectively log an accepted request.""" - - if self.server.logRequests: - BaseHTTPRequestHandler.log_request(self, code, size) - -class SimpleXMLRPCServer(socketserver.TCPServer, - SimpleXMLRPCDispatcher): - """Simple XML-RPC server. - - Simple XML-RPC server that allows functions and a single instance - to be installed to handle requests. The default implementation - attempts to dispatch XML-RPC calls to the functions or instance - installed in the server. Override the _dispatch method inherited - from SimpleXMLRPCDispatcher to change this behavior. - """ - - allow_reuse_address = True - - # Warning: this is for debugging purposes only! Never set this to True in - # production code, as will be sending out sensitive information (exception - # and stack trace details) when exceptions are raised inside - # SimpleXMLRPCRequestHandler.do_POST - _send_traceback_header = False - - def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - self.logRequests = logRequests - - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) - socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) - - # [Bug #1222790] If possible, set close-on-exec flag; if a - # method spawns a subprocess, the subprocess shouldn't have - # the listening socket open. - if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): - flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) - flags |= fcntl.FD_CLOEXEC - fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) - -class MultiPathXMLRPCServer(SimpleXMLRPCServer): - """Multipath XML-RPC Server - This specialization of SimpleXMLRPCServer allows the user to create - multiple Dispatcher instances and assign them to different - HTTP request paths. This makes it possible to run two or more - 'virtual XML-RPC servers' at the same port. - Make sure that the requestHandler accepts the paths in question. - """ - def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, - encoding, bind_and_activate, use_builtin_types) - self.dispatchers = {} - self.allow_none = allow_none - self.encoding = encoding or 'utf-8' - - def add_dispatcher(self, path, dispatcher): - self.dispatchers[path] = dispatcher - return dispatcher - - def get_dispatcher(self, path): - return self.dispatchers[path] - - def _marshaled_dispatch(self, data, dispatch_method = None, path = None): - try: - response = self.dispatchers[path]._marshaled_dispatch( - data, dispatch_method, path) - except: - # report low level exception back to server - # (each dispatcher should have handled their own - # exceptions) - exc_type, exc_value = sys.exc_info()[:2] - response = dumps( - Fault(1, "%s:%s" % (exc_type, exc_value)), - encoding=self.encoding, allow_none=self.allow_none) - response = response.encode(self.encoding) - return response - -class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): - """Simple handler for XML-RPC data passed through CGI.""" - - def __init__(self, allow_none=False, encoding=None, use_builtin_types=False): - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) - - def handle_xmlrpc(self, request_text): - """Handle a single XML-RPC request""" - - response = self._marshaled_dispatch(request_text) - - print('Content-Type: text/xml') - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def handle_get(self): - """Handle a single HTTP GET request. - - Default implementation indicates an error because - XML-RPC uses the POST method. - """ - - code = 400 - message, explain = BaseHTTPRequestHandler.responses[code] - - response = http_server.DEFAULT_ERROR_MESSAGE % \ - { - 'code' : code, - 'message' : message, - 'explain' : explain - } - response = response.encode('utf-8') - print('Status: %d %s' % (code, message)) - print('Content-Type: %s' % http_server.DEFAULT_ERROR_CONTENT_TYPE) - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def handle_request(self, request_text=None): - """Handle a single XML-RPC request passed through a CGI post method. - - If no XML data is given then it is read from stdin. The resulting - XML-RPC response is printed to stdout along with the correct HTTP - headers. - """ - - if request_text is None and \ - os.environ.get('REQUEST_METHOD', None) == 'GET': - self.handle_get() - else: - # POST data is normally available through stdin - try: - length = int(os.environ.get('CONTENT_LENGTH', None)) - except (ValueError, TypeError): - length = -1 - if request_text is None: - request_text = sys.stdin.read(length) - - self.handle_xmlrpc(request_text) - - -# ----------------------------------------------------------------------------- -# Self documenting XML-RPC Server. - -class ServerHTMLDoc(pydoc.HTMLDoc): - """Class used to generate pydoc HTML document for a server""" - - def markup(self, text, escape=None, funcs={}, classes={}, methods={}): - """Mark up some plain text, given a context of symbols to look for. - Each context dictionary maps object names to anchor names.""" - escape = escape or self.escape - results = [] - here = 0 - - # XXX Note that this regular expression does not allow for the - # hyperlinking of arbitrary strings being used as method - # names. Only methods with names consisting of word characters - # and '.'s are hyperlinked. - pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' - r'RFC[- ]?(\d+)|' - r'PEP[- ]?(\d+)|' - r'(self\.)?((?:\w|\.)+))\b') - while 1: - match = pattern.search(text, here) - if not match: break - start, end = match.span() - results.append(escape(text[here:start])) - - all, scheme, rfc, pep, selfdot, name = match.groups() - if scheme: - url = escape(all).replace('"', '"') - results.append('%s' % (url, url)) - elif rfc: - url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) - results.append('%s' % (url, escape(all))) - elif pep: - url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) - results.append('%s' % (url, escape(all))) - elif text[end:end+1] == '(': - results.append(self.namelink(name, methods, funcs, classes)) - elif selfdot: - results.append('self.%s' % name) - else: - results.append(self.namelink(name, classes)) - here = end - results.append(escape(text[here:])) - return ''.join(results) - - def docroutine(self, object, name, mod=None, - funcs={}, classes={}, methods={}, cl=None): - """Produce HTML documentation for a function or method object.""" - - anchor = (cl and cl.__name__ or '') + '-' + name - note = '' - - title = '%s' % ( - self.escape(anchor), self.escape(name)) - - if inspect.ismethod(object): - args = inspect.getfullargspec(object) - # exclude the argument bound to the instance, it will be - # confusing to the non-Python user - argspec = inspect.formatargspec ( - args.args[1:], - args.varargs, - args.varkw, - args.defaults, - annotations=args.annotations, - formatvalue=self.formatvalue - ) - elif inspect.isfunction(object): - args = inspect.getfullargspec(object) - argspec = inspect.formatargspec( - args.args, args.varargs, args.varkw, args.defaults, - annotations=args.annotations, - formatvalue=self.formatvalue) - else: - argspec = '(...)' - - if isinstance(object, tuple): - argspec = object[0] or argspec - docstring = object[1] or "" - else: - docstring = pydoc.getdoc(object) - - decl = title + argspec + (note and self.grey( - '%s' % note)) - - doc = self.markup( - docstring, self.preformat, funcs, classes, methods) - doc = doc and '

%s
' % doc - return '
%s
%s
\n' % (decl, doc) - - def docserver(self, server_name, package_documentation, methods): - """Produce HTML documentation for an XML-RPC server.""" - - fdict = {} - for key, value in methods.items(): - fdict[key] = '#-' + key - fdict[value] = fdict[key] - - server_name = self.escape(server_name) - head = '%s' % server_name - result = self.heading(head, '#ffffff', '#7799ee') - - doc = self.markup(package_documentation, self.preformat, fdict) - doc = doc and '%s' % doc - result = result + '

%s

\n' % doc - - contents = [] - method_items = sorted(methods.items()) - for key, value in method_items: - contents.append(self.docroutine(value, key, funcs=fdict)) - result = result + self.bigsection( - 'Methods', '#ffffff', '#eeaa77', ''.join(contents)) - - return result - -class XMLRPCDocGenerator(object): - """Generates documentation for an XML-RPC server. - - This class is designed as mix-in and should not - be constructed directly. - """ - - def __init__(self): - # setup variables used for HTML documentation - self.server_name = 'XML-RPC Server Documentation' - self.server_documentation = \ - "This server exports the following methods through the XML-RPC "\ - "protocol." - self.server_title = 'XML-RPC Server Documentation' - - def set_server_title(self, server_title): - """Set the HTML title of the generated server documentation""" - - self.server_title = server_title - - def set_server_name(self, server_name): - """Set the name of the generated HTML server documentation""" - - self.server_name = server_name - - def set_server_documentation(self, server_documentation): - """Set the documentation string for the entire server.""" - - self.server_documentation = server_documentation - - def generate_html_documentation(self): - """generate_html_documentation() => html documentation for the server - - Generates HTML documentation for the server using introspection for - installed functions and instances that do not implement the - _dispatch method. Alternatively, instances can choose to implement - the _get_method_argstring(method_name) method to provide the - argument string used in the documentation and the - _methodHelp(method_name) method to provide the help text used - in the documentation.""" - - methods = {} - - for method_name in self.system_listMethods(): - if method_name in self.funcs: - method = self.funcs[method_name] - elif self.instance is not None: - method_info = [None, None] # argspec, documentation - if hasattr(self.instance, '_get_method_argstring'): - method_info[0] = self.instance._get_method_argstring(method_name) - if hasattr(self.instance, '_methodHelp'): - method_info[1] = self.instance._methodHelp(method_name) - - method_info = tuple(method_info) - if method_info != (None, None): - method = method_info - elif not hasattr(self.instance, '_dispatch'): - try: - method = resolve_dotted_attribute( - self.instance, - method_name - ) - except AttributeError: - method = method_info - else: - method = method_info - else: - assert 0, "Could not find method in self.functions and no "\ - "instance installed" - - methods[method_name] = method - - documenter = ServerHTMLDoc() - documentation = documenter.docserver( - self.server_name, - self.server_documentation, - methods - ) - - return documenter.page(self.server_title, documentation) - -class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): - """XML-RPC and documentation request handler class. - - Handles all HTTP POST requests and attempts to decode them as - XML-RPC requests. - - Handles all HTTP GET requests and interprets them as requests - for documentation. - """ - - def do_GET(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - response = self.server.generate_html_documentation().encode('utf-8') - self.send_response(200) - self.send_header("Content-type", "text/html") - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - -class DocXMLRPCServer( SimpleXMLRPCServer, - XMLRPCDocGenerator): - """XML-RPC and HTML documentation server. - - Adds the ability to serve server documentation to the capabilities - of SimpleXMLRPCServer. - """ - - def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, - allow_none, encoding, bind_and_activate, - use_builtin_types) - XMLRPCDocGenerator.__init__(self) - -class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, - XMLRPCDocGenerator): - """Handler for XML-RPC data and documentation requests passed through - CGI""" - - def handle_get(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - - response = self.generate_html_documentation().encode('utf-8') - - print('Content-Type: text/html') - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def __init__(self): - CGIXMLRPCRequestHandler.__init__(self) - XMLRPCDocGenerator.__init__(self) - - -if __name__ == '__main__': - import datetime - - class ExampleService: - def getData(self): - return '42' - - class currentTime: - @staticmethod - def getCurrentTime(): - return datetime.datetime.now() - - server = SimpleXMLRPCServer(("localhost", 8000)) - server.register_function(pow) - server.register_function(lambda x,y: x+y, 'add') - server.register_instance(ExampleService(), allow_dotted_names=True) - server.register_multicall_functions() - print('Serving XML-RPC on localhost port 8000') - print('It is advisable to run this example server within a secure, closed network.') - try: - server.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - server.server_close() - sys.exit(0) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/disabled.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/disabled.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/disabled.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/disabled.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -""" -This disables builtin functions (and one exception class) which are -removed from Python 3.3. - -This module is designed to be used like this:: - - from future.builtins.disabled import * - -This disables the following obsolete Py2 builtin functions:: - - apply, cmp, coerce, execfile, file, input, long, - raw_input, reduce, reload, unicode, xrange - -We don't hack __builtin__, which is very fragile because it contaminates -imported modules too. Instead, we just create new functions with -the same names as the obsolete builtins from Python 2 which raise -NameError exceptions when called. - -Note that both ``input()`` and ``raw_input()`` are among the disabled -functions (in this module). Although ``input()`` exists as a builtin in -Python 3, the Python 2 ``input()`` builtin is unsafe to use because it -can lead to shell injection. Therefore we shadow it by default upon ``from -future.builtins.disabled import *``, in case someone forgets to import our -replacement ``input()`` somehow and expects Python 3 semantics. - -See the ``future.builtins.misc`` module for a working version of -``input`` with Python 3 semantics. - -(Note that callable() is not among the functions disabled; this was -reintroduced into Python 3.2.) - -This exception class is also disabled: - - StandardError - -""" - -from __future__ import division, absolute_import, print_function - -from future import utils - - -OBSOLETE_BUILTINS = ['apply', 'chr', 'cmp', 'coerce', 'execfile', 'file', - 'input', 'long', 'raw_input', 'reduce', 'reload', - 'unicode', 'xrange', 'StandardError'] - - -def disabled_function(name): - ''' - Returns a function that cannot be called - ''' - def disabled(*args, **kwargs): - ''' - A function disabled by the ``future`` module. This function is - no longer a builtin in Python 3. - ''' - raise NameError('obsolete Python 2 builtin {0} is disabled'.format(name)) - return disabled - - -if not utils.PY3: - for fname in OBSOLETE_BUILTINS: - locals()[fname] = disabled_function(fname) - __all__ = OBSOLETE_BUILTINS -else: - __all__ = [] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -""" -A module that brings in equivalents of the new and modified Python 3 -builtins into Py2. Has no effect on Py3. - -See the docs `here `_ -(``docs/what-else.rst``) for more information. - -""" - -from future.builtins.iterators import (filter, map, zip) -# The isinstance import is no longer needed. We provide it only for -# backward-compatibility with future v0.8.2. It will be removed in future v1.0. -from future.builtins.misc import (ascii, chr, hex, input, isinstance, next, - oct, open, pow, round, super) -from future.utils import PY3 - -if PY3: - import builtins - bytes = builtins.bytes - dict = builtins.dict - int = builtins.int - list = builtins.list - object = builtins.object - range = builtins.range - str = builtins.str - __all__ = [] -else: - from future.types import (newbytes as bytes, - newdict as dict, - newint as int, - newlist as list, - newobject as object, - newrange as range, - newstr as str) -from future import utils - - -if not utils.PY3: - # We only import names that shadow the builtins on Py2. No other namespace - # pollution on Py2. - - # Only shadow builtins on Py2; no new names - __all__ = ['filter', 'map', 'zip', - 'ascii', 'chr', 'hex', 'input', 'next', 'oct', 'open', 'pow', - 'round', 'super', - 'bytes', 'dict', 'int', 'list', 'object', 'range', 'str', - ] - -else: - # No namespace pollution on Py3 - __all__ = [] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/iterators.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/iterators.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/iterators.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/iterators.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -""" -This module is designed to be used as follows:: - - from future.builtins.iterators import * - -And then, for example:: - - for i in range(10**15): - pass - - for (a, b) in zip(range(10**15), range(-10**15, 0)): - pass - -Note that this is standard Python 3 code, plus some imports that do -nothing on Python 3. - -The iterators this brings in are:: - -- ``range`` -- ``filter`` -- ``map`` -- ``zip`` - -On Python 2, ``range`` is a pure-Python backport of Python 3's ``range`` -iterator with slicing support. The other iterators (``filter``, ``map``, -``zip``) are from the ``itertools`` module on Python 2. On Python 3 these -are available in the module namespace but not exported for * imports via -__all__ (zero no namespace pollution). - -Note that these are also available in the standard library -``future_builtins`` module on Python 2 -- but not Python 3, so using -the standard library version is not portable, nor anywhere near complete. -""" - -from __future__ import division, absolute_import, print_function - -import itertools -from future import utils - -if not utils.PY3: - filter = itertools.ifilter - map = itertools.imap - from future.types import newrange as range - zip = itertools.izip - __all__ = ['filter', 'map', 'range', 'zip'] -else: - import builtins - filter = builtins.filter - map = builtins.map - range = builtins.range - zip = builtins.zip - __all__ = [] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/misc.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/misc.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/misc.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/misc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -""" -A module that brings in equivalents of various modified Python 3 builtins -into Py2. Has no effect on Py3. - -The builtin functions are: - -- ``ascii`` (from Py2's future_builtins module) -- ``hex`` (from Py2's future_builtins module) -- ``oct`` (from Py2's future_builtins module) -- ``chr`` (equivalent to ``unichr`` on Py2) -- ``input`` (equivalent to ``raw_input`` on Py2) -- ``next`` (calls ``__next__`` if it exists, else ``next`` method) -- ``open`` (equivalent to io.open on Py2) -- ``super`` (backport of Py3's magic zero-argument super() function -- ``round`` (new "Banker's Rounding" behaviour from Py3) - -``isinstance`` is also currently exported for backwards compatibility -with v0.8.2, although this has been deprecated since v0.9. - - -input() -------- -Like the new ``input()`` function from Python 3 (without eval()), except -that it returns bytes. Equivalent to Python 2's ``raw_input()``. - -Warning: By default, importing this module *removes* the old Python 2 -input() function entirely from ``__builtin__`` for safety. This is -because forgetting to import the new ``input`` from ``future`` might -otherwise lead to a security vulnerability (shell injection) on Python 2. - -To restore it, you can retrieve it yourself from -``__builtin__._old_input``. - -Fortunately, ``input()`` seems to be seldom used in the wild in Python -2... - -""" - -from future import utils - - -if utils.PY2: - from io import open - from future_builtins import ascii, oct, hex - from __builtin__ import unichr as chr, pow as _builtin_pow - import __builtin__ - - # Only for backward compatibility with future v0.8.2: - isinstance = __builtin__.isinstance - - # Warning: Python 2's input() is unsafe and MUST not be able to be used - # accidentally by someone who expects Python 3 semantics but forgets - # to import it on Python 2. Versions of ``future`` prior to 0.11 - # deleted it from __builtin__. Now we keep in __builtin__ but shadow - # the name like all others. Just be sure to import ``input``. - - input = raw_input - - from future.builtins.newnext import newnext as next - from future.builtins.newround import newround as round - from future.builtins.newsuper import newsuper as super - from future.types.newint import newint - - _SENTINEL = object() - - def pow(x, y, z=_SENTINEL): - """ - pow(x, y[, z]) -> number - - With two arguments, equivalent to x**y. With three arguments, - equivalent to (x**y) % z, but may be more efficient (e.g. for ints). - """ - # Handle newints - if isinstance(x, newint): - x = long(x) - if isinstance(y, newint): - y = long(y) - if isinstance(z, newint): - z = long(z) - - try: - if z == _SENTINEL: - return _builtin_pow(x, y) - else: - return _builtin_pow(x, y, z) - except ValueError: - if z == _SENTINEL: - return _builtin_pow(x+0j, y) - else: - return _builtin_pow(x+0j, y, z) - - # ``future`` doesn't support Py3.0/3.1. If we ever did, we'd add this: - # callable = __builtin__.callable - - __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct', - 'open', 'pow', 'round', 'super'] - -else: - import builtins - ascii = builtins.ascii - chr = builtins.chr - hex = builtins.hex - input = builtins.input - next = builtins.next - # Only for backward compatibility with future v0.8.2: - isinstance = builtins.isinstance - oct = builtins.oct - open = builtins.open - pow = builtins.pow - round = builtins.round - super = builtins.super - - __all__ = [] - - # The callable() function was removed from Py3.0 and 3.1 and - # reintroduced into Py3.2+. ``future`` doesn't support Py3.0/3.1. If we ever - # did, we'd add this: - # try: - # callable = builtins.callable - # except AttributeError: - # # Definition from Pandas - # def callable(obj): - # return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - # __all__.append('callable') diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/newnext.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/newnext.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/newnext.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/newnext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -''' -This module provides a newnext() function in Python 2 that mimics the -behaviour of ``next()`` in Python 3, falling back to Python 2's behaviour for -compatibility if this fails. - -``newnext(iterator)`` calls the iterator's ``__next__()`` method if it exists. If this -doesn't exist, it falls back to calling a ``next()`` method. - -For example: - - >>> class Odds(object): - ... def __init__(self, start=1): - ... self.value = start - 2 - ... def __next__(self): # note the Py3 interface - ... self.value += 2 - ... return self.value - ... def __iter__(self): - ... return self - ... - >>> iterator = Odds() - >>> next(iterator) - 1 - >>> next(iterator) - 3 - -If you are defining your own custom iterator class as above, it is preferable -to explicitly decorate the class with the @implements_iterator decorator from -``future.utils`` as follows: - - >>> @implements_iterator - ... class Odds(object): - ... # etc - ... pass - -This next() function is primarily for consuming iterators defined in Python 3 -code elsewhere that we would like to run on Python 2 or 3. -''' - -_builtin_next = next - -_SENTINEL = object() - -def newnext(iterator, default=_SENTINEL): - """ - next(iterator[, default]) - - Return the next item from the iterator. If default is given and the iterator - is exhausted, it is returned instead of raising StopIteration. - """ - - # args = [] - # if default is not _SENTINEL: - # args.append(default) - try: - try: - return iterator.__next__() - except AttributeError: - try: - return iterator.next() - except AttributeError: - raise TypeError("'{0}' object is not an iterator".format( - iterator.__class__.__name__)) - except StopIteration as e: - if default is _SENTINEL: - raise e - else: - return default - - -__all__ = ['newnext'] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/newround.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/newround.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/newround.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/newround.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -""" -``python-future``: pure Python implementation of Python 3 round(). -""" - -from future.utils import PYPY, PY26, bind_method - -# Use the decimal module for simplicity of implementation (and -# hopefully correctness). -from decimal import Decimal, ROUND_HALF_EVEN - - -def newround(number, ndigits=None): - """ - See Python 3 documentation: uses Banker's Rounding. - - Delegates to the __round__ method if for some reason this exists. - - If not, rounds a number to a given precision in decimal digits (default - 0 digits). This returns an int when called with one argument, - otherwise the same type as the number. ndigits may be negative. - - See the test_round method in future/tests/test_builtins.py for - examples. - """ - return_int = False - if ndigits is None: - return_int = True - ndigits = 0 - if hasattr(number, '__round__'): - return number.__round__(ndigits) - - if ndigits < 0: - raise NotImplementedError('negative ndigits not supported yet') - exponent = Decimal('10') ** (-ndigits) - - if PYPY: - # Work around issue #24: round() breaks on PyPy with NumPy's types - if 'numpy' in repr(type(number)): - number = float(number) - - if not PY26: - d = Decimal.from_float(number).quantize(exponent, - rounding=ROUND_HALF_EVEN) - else: - d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN) - - if return_int: - return int(d) - else: - return float(d) - - -### From Python 2.7's decimal.py. Only needed to support Py2.6: - -def from_float_26(f): - """Converts a float to a decimal number, exactly. - - Note that Decimal.from_float(0.1) is not the same as Decimal('0.1'). - Since 0.1 is not exactly representable in binary floating point, the - value is stored as the nearest representable value which is - 0x1.999999999999ap-4. The exact equivalent of the value in decimal - is 0.1000000000000000055511151231257827021181583404541015625. - - >>> Decimal.from_float(0.1) - Decimal('0.1000000000000000055511151231257827021181583404541015625') - >>> Decimal.from_float(float('nan')) - Decimal('NaN') - >>> Decimal.from_float(float('inf')) - Decimal('Infinity') - >>> Decimal.from_float(-float('inf')) - Decimal('-Infinity') - >>> Decimal.from_float(-0.0) - Decimal('-0') - - """ - import math as _math - from decimal import _dec_from_triple # only available on Py2.6 and Py2.7 (not 3.3) - - if isinstance(f, (int, long)): # handle integer inputs - return Decimal(f) - if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float - return Decimal(repr(f)) - if _math.copysign(1.0, f) == 1.0: - sign = 0 - else: - sign = 1 - n, d = abs(f).as_integer_ratio() - # int.bit_length() method doesn't exist on Py2.6: - def bit_length(d): - if d != 0: - return len(bin(abs(d))) - 2 - else: - return 0 - k = bit_length(d) - 1 - result = _dec_from_triple(sign, str(n*5**k), -k) - return result - - -__all__ = ['newround'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/newsuper.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/newsuper.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/builtins/newsuper.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/builtins/newsuper.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,114 +0,0 @@ -''' -This module provides a newsuper() function in Python 2 that mimics the -behaviour of super() in Python 3. It is designed to be used as follows: - - from __future__ import division, absolute_import, print_function - from future.builtins import super - -And then, for example: - - class VerboseList(list): - def append(self, item): - print('Adding an item') - super().append(item) # new simpler super() function - -Importing this module on Python 3 has no effect. - -This is based on (i.e. almost identical to) Ryan Kelly's magicsuper -module here: - - https://github.com/rfk/magicsuper.git - -Excerpts from Ryan's docstring: - - "Of course, you can still explicitly pass in the arguments if you want - to do something strange. Sometimes you really do want that, e.g. to - skip over some classes in the method resolution order. - - "How does it work? By inspecting the calling frame to determine the - function object being executed and the object on which it's being - called, and then walking the object's __mro__ chain to find out where - that function was defined. Yuck, but it seems to work..." -''' - -from __future__ import absolute_import -import sys -from types import FunctionType - -from future.utils import PY3, PY26 - - -_builtin_super = super - -_SENTINEL = object() - -def newsuper(typ=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): - '''Like builtin super(), but capable of magic. - - This acts just like the builtin super() function, but if called - without any arguments it attempts to infer them at runtime. - ''' - # Infer the correct call if used without arguments. - if typ is _SENTINEL: - # We'll need to do some frame hacking. - f = sys._getframe(framedepth) - - try: - # Get the function's first positional argument. - type_or_obj = f.f_locals[f.f_code.co_varnames[0]] - except (IndexError, KeyError,): - raise RuntimeError('super() used in a function with no args') - - try: - # Get the MRO so we can crawl it. - mro = type_or_obj.__mro__ - except (AttributeError, RuntimeError): # see issue #160 - try: - mro = type_or_obj.__class__.__mro__ - except AttributeError: - raise RuntimeError('super() used with a non-newstyle class') - - # A ``for...else`` block? Yes! It's odd, but useful. - # If unfamiliar with for...else, see: - # - # http://psung.blogspot.com/2007/12/for-else-in-python.html - for typ in mro: - # Find the class that owns the currently-executing method. - for meth in typ.__dict__.values(): - # Drill down through any wrappers to the underlying func. - # This handles e.g. classmethod() and staticmethod(). - try: - while not isinstance(meth,FunctionType): - if isinstance(meth, property): - # Calling __get__ on the property will invoke - # user code which might throw exceptions or have - # side effects - meth = meth.fget - else: - try: - meth = meth.__func__ - except AttributeError: - meth = meth.__get__(type_or_obj, typ) - except (AttributeError, TypeError): - continue - if meth.func_code is f.f_code: - break # Aha! Found you. - else: - continue # Not found! Move onto the next class in MRO. - break # Found! Break out of the search loop. - else: - raise RuntimeError('super() called outside a method') - - # Dispatch to builtin super(). - if type_or_obj is not _SENTINEL: - return _builtin_super(typ, type_or_obj) - return _builtin_super(typ) - - -def superm(*args, **kwds): - f = sys._getframe(1) - nm = f.f_code.co_name - return getattr(newsuper(framedepth=2),nm)(*args, **kwds) - - -__all__ = ['newsuper'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -""" -future: Easy, safe support for Python 2/3 compatibility -======================================================= - -``future`` is the missing compatibility layer between Python 2 and Python -3. It allows you to use a single, clean Python 3.x-compatible codebase to -support both Python 2 and Python 3 with minimal overhead. - -It is designed to be used as follows:: - - from __future__ import (absolute_import, division, - print_function, unicode_literals) - from builtins import ( - bytes, dict, int, list, object, range, str, - ascii, chr, hex, input, next, oct, open, - pow, round, super, - filter, map, zip) - -followed by predominantly standard, idiomatic Python 3 code that then runs -similarly on Python 2.6/2.7 and Python 3.3+. - -The imports have no effect on Python 3. On Python 2, they shadow the -corresponding builtins, which normally have different semantics on Python 3 -versus 2, to provide their Python 3 semantics. - - -Standard library reorganization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``future`` supports the standard library reorganization (PEP 3108) through the -following Py3 interfaces: - - >>> # Top-level packages with Py3 names provided on Py2: - >>> import html.parser - >>> import queue - >>> import tkinter.dialog - >>> import xmlrpc.client - >>> # etc. - - >>> # Aliases provided for extensions to existing Py2 module names: - >>> from future.standard_library import install_aliases - >>> install_aliases() - - >>> from collections import Counter, OrderedDict # backported to Py2.6 - >>> from collections import UserDict, UserList, UserString - >>> import urllib.request - >>> from itertools import filterfalse, zip_longest - >>> from subprocess import getoutput, getstatusoutput - - -Automatic conversion --------------------- - -An included script called `futurize -`_ aids in converting -code (from either Python 2 or Python 3) to code compatible with both -platforms. It is similar to ``python-modernize`` but goes further in -providing Python 3 compatibility through the use of the backported types -and builtin functions in ``future``. - - -Documentation -------------- - -See: http://python-future.org - - -Credits -------- - -:Author: Ed Schofield -:Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte - Ltd, Singapore. http://pythoncharmers.com -:Others: See docs/credits.rst or http://python-future.org/credits.html - - -Licensing ---------- -Copyright 2013-2016 Python Charmers Pty Ltd, Australia. -The software is distributed under an MIT licence. See LICENSE.txt. - -""" - -__title__ = 'future' -__author__ = 'Ed Schofield' -__license__ = 'MIT' -__copyright__ = 'Copyright 2013-2016 Python Charmers Pty Ltd' -__ver_major__ = 0 -__ver_minor__ = 16 -__ver_patch__ = 0 -__ver_sub__ = '' -__version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__, - __ver_patch__, __ver_sub__) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/builtins.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/builtins.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from builtins import * -else: - __future_module__ = True - from __builtin__ import * - # Overwrite any old definitions with the equivalent future.builtins ones: - from future.builtins import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/collections.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/collections.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/collections.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/collections.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -from __future__ import absolute_import -import sys - -from future.utils import PY2, PY26 -__future_module__ = True - -from collections import * - -if PY2: - from UserDict import UserDict - from UserList import UserList - from UserString import UserString - -if PY26: - from future.backports.misc import OrderedDict, Counter - -if sys.version_info < (3, 3): - from future.backports.misc import ChainMap, _count_elements diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/configparser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/configparser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/configparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/configparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY2 - -if PY2: - from ConfigParser import * -else: - from configparser import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/copyreg.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/copyreg.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/copyreg.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/copyreg.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from copyreg import * -else: - __future_module__ = True - from copy_reg import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/dumb.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/dumb.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/dumb.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/dumb.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from dbm.dumb import * -else: - __future_module__ = True - from dumbdbm import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/gnu.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/gnu.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/gnu.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/gnu.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from dbm.gnu import * -else: - __future_module__ = True - from gdbm import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from dbm import * -else: - __future_module__ = True - from whichdb import * - from anydbm import * - -# Py3.3's dbm/__init__.py imports ndbm but doesn't expose it via __all__. -# In case some (badly written) code depends on dbm.ndbm after import dbm, -# we simulate this: -if PY3: - from dbm import ndbm -else: - try: - from future.moves.dbm import ndbm - except ImportError: - ndbm = None diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/ndbm.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/ndbm.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/dbm/ndbm.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/dbm/ndbm.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from dbm.ndbm import * -else: - __future_module__ = True - from dbm import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/_dummy_thread.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/_dummy_thread.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/_dummy_thread.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/_dummy_thread.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _dummy_thread import * -else: - __future_module__ = True - from dummy_thread import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/html/entities.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/html/entities.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/html/entities.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/html/entities.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from html.entities import * -else: - __future_module__ = True - from htmlentitydefs import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/html/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/html/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/html/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/html/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 -__future_module__ = True - -if PY3: - from html import * -else: - # cgi.escape isn't good enough for the single Py3.3 html test to pass. - # Define it inline here instead. From the Py3.4 stdlib. Note that the - # html.escape() function from the Py3.3 stdlib is not suitable for use on - # Py2.x. - """ - General functions for HTML manipulation. - """ - - def escape(s, quote=True): - """ - Replace special characters "&", "<" and ">" to HTML-safe sequences. - If the optional flag quote is true (the default), the quotation mark - characters, both double quote (") and single quote (') characters are also - translated. - """ - s = s.replace("&", "&") # Must be done first! - s = s.replace("<", "<") - s = s.replace(">", ">") - if quote: - s = s.replace('"', """) - s = s.replace('\'', "'") - return s - - __all__ = ['escape'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/html/parser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/html/parser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/html/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/html/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 -__future_module__ = True - -if PY3: - from html.parser import * -else: - from HTMLParser import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/client.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/client.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from future.utils import PY3 - -if PY3: - from http.client import * -else: - from httplib import * - from httplib import HTTPMessage - __future_module__ = True diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/cookiejar.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/cookiejar.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/cookiejar.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/cookiejar.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from http.cookiejar import * -else: - __future_module__ = True - from cookielib import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/cookies.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/cookies.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/cookies.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/cookies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from http.cookies import * -else: - __future_module__ = True - from Cookie import * - from Cookie import Morsel # left out of __all__ on Py2.7! diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -from future.utils import PY3 - -if not PY3: - __future_module__ = True diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/server.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/server.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/http/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/http/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from http.server import * -else: - __future_module__ = True - from BaseHTTPServer import * - from CGIHTTPServer import * - from SimpleHTTPServer import * - try: - from CGIHTTPServer import _url_collapse_path # needed for a test - except ImportError: - try: - # Python 2.7.0 to 2.7.3 - from CGIHTTPServer import ( - _url_collapse_path_split as _url_collapse_path) - except ImportError: - # Doesn't exist on Python 2.6.x. Ignore it. - pass diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -# future.moves package -from __future__ import absolute_import -import sys -__future_module__ = True -from future.standard_library import import_top_level_modules - -if sys.version_info[0] == 3: - import_top_level_modules() diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/itertools.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/itertools.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/itertools.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/itertools.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import - -from itertools import * -try: - zip_longest = izip_longest - filterfalse = ifilterfalse -except NameError: - pass diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/_markupbase.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/_markupbase.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/_markupbase.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/_markupbase.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _markupbase import * -else: - __future_module__ = True - from markupbase import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/pickle.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/pickle.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/pickle.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/pickle.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from pickle import * -else: - __future_module__ = True - try: - from cPickle import * - except ImportError: - from pickle import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/queue.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/queue.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/queue.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/queue.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from queue import * -else: - __future_module__ = True - from Queue import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/reprlib.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/reprlib.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/reprlib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/reprlib.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from reprlib import * -else: - __future_module__ = True - from repr import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/socketserver.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/socketserver.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/socketserver.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/socketserver.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from socketserver import * -else: - __future_module__ = True - from SocketServer import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/subprocess.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/subprocess.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/subprocess.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/subprocess.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY2, PY26 - -from subprocess import * - -if PY2: - __future_module__ = True - from commands import getoutput, getstatusoutput - -if PY26: - from future.backports.misc import check_output diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/sys.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/sys.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/sys.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/sys.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY2 - -from sys import * - -if PY2: - from __builtin__ import intern diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/test/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/test/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/test/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/test/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if not PY3: - __future_module__ = True diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/test/support.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/test/support.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/test/support.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/test/support.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.standard_library import suspend_hooks -from future.utils import PY3 - -if PY3: - from test.support import * -else: - __future_module__ = True - with suspend_hooks(): - from test.test_support import * - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/_thread.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/_thread.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/_thread.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/_thread.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _thread import * -else: - __future_module__ = True - from thread import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.colorchooser import * -else: - try: - from tkColorChooser import * - except ImportError: - raise ImportError('The tkColorChooser module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/commondialog.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/commondialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/commondialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/commondialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.commondialog import * -else: - try: - from tkCommonDialog import * - except ImportError: - raise ImportError('The tkCommonDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/constants.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/constants.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/constants.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/constants.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.constants import * -else: - try: - from Tkconstants import * - except ImportError: - raise ImportError('The Tkconstants module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/dialog.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/dialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/dialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/dialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dialog import * -else: - try: - from Dialog import * - except ImportError: - raise ImportError('The Dialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/dnd.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/dnd.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/dnd.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/dnd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dnd import * -else: - try: - from Tkdnd import * - except ImportError: - raise ImportError('The Tkdnd module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/filedialog.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/filedialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/filedialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/filedialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.filedialog import * -else: - try: - from FileDialog import * - except ImportError: - raise ImportError('The FileDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/font.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/font.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/font.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/font.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.font import * -else: - try: - from tkFont import * - except ImportError: - raise ImportError('The tkFont module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 -__future_module__ = True - -if not PY3: - from Tkinter import * - from Tkinter import (_cnfmerge, _default_root, _flatten, _join, _setit, - _splitdict, _stringify, _support_default_root, _test, - _tkinter) -else: - from tkinter import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/messagebox.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/messagebox.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/messagebox.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/messagebox.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.messagebox import * -else: - try: - from tkMessageBox import * - except ImportError: - raise ImportError('The tkMessageBox module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.scrolledtext import * -else: - try: - from ScrolledText import * - except ImportError: - raise ImportError('The ScrolledText module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.simpledialog import * -else: - try: - from SimpleDialog import * - except ImportError: - raise ImportError('The SimpleDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/tix.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/tix.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/tix.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/tix.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.tix import * -else: - try: - from Tix import * - except ImportError: - raise ImportError('The Tix module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/ttk.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/ttk.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/tkinter/ttk.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/tkinter/ttk.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.ttk import * -else: - try: - from ttk import * - except ImportError: - raise ImportError('The ttk module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/error.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/error.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/error.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/error.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -from __future__ import absolute_import -from future.standard_library import suspend_hooks - -from future.utils import PY3 - -if PY3: - from urllib.error import * -else: - __future_module__ = True - - # We use this method to get at the original Py2 urllib before any renaming magic - # ContentTooShortError = sys.py2_modules['urllib'].ContentTooShortError - - with suspend_hooks(): - from urllib import ContentTooShortError - from urllib2 import URLError, HTTPError diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if not PY3: - __future_module__ = True - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/parse.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/parse.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/parse.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/parse.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -from __future__ import absolute_import -from future.standard_library import suspend_hooks - -from future.utils import PY3 - -if PY3: - from urllib.parse import * -else: - __future_module__ = True - from urlparse import (ParseResult, SplitResult, parse_qs, parse_qsl, - urldefrag, urljoin, urlparse, urlsplit, - urlunparse, urlunsplit) - - # we use this method to get at the original py2 urllib before any renaming - # quote = sys.py2_modules['urllib'].quote - # quote_plus = sys.py2_modules['urllib'].quote_plus - # unquote = sys.py2_modules['urllib'].unquote - # unquote_plus = sys.py2_modules['urllib'].unquote_plus - # urlencode = sys.py2_modules['urllib'].urlencode - # splitquery = sys.py2_modules['urllib'].splitquery - - with suspend_hooks(): - from urllib import (quote, - quote_plus, - unquote, - unquote_plus, - urlencode, - splitquery) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/request.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/request.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/request.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/request.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -from __future__ import absolute_import - -from future.standard_library import suspend_hooks -from future.utils import PY3 - -if PY3: - from urllib.request import * - # This aren't in __all__: - from urllib.request import (getproxies, - pathname2url, - proxy_bypass, - quote, - request_host, - splitattr, - splithost, - splitpasswd, - splitport, - splitquery, - splittag, - splittype, - splituser, - splitvalue, - thishost, - to_bytes, - unquote, - unwrap, - url2pathname, - urlcleanup, - urljoin, - urlopen, - urlparse, - urlretrieve, - urlsplit, - urlunparse) -else: - __future_module__ = True - with suspend_hooks(): - from urllib import * - from urllib2 import * - from urlparse import * - - # Rename: - from urllib import toBytes # missing from __all__ on Py2.6 - to_bytes = toBytes - - # from urllib import (pathname2url, - # url2pathname, - # getproxies, - # urlretrieve, - # urlcleanup, - # URLopener, - # FancyURLopener, - # proxy_bypass) - - # from urllib2 import ( - # AbstractBasicAuthHandler, - # AbstractDigestAuthHandler, - # BaseHandler, - # CacheFTPHandler, - # FileHandler, - # FTPHandler, - # HTTPBasicAuthHandler, - # HTTPCookieProcessor, - # HTTPDefaultErrorHandler, - # HTTPDigestAuthHandler, - # HTTPErrorProcessor, - # HTTPHandler, - # HTTPPasswordMgr, - # HTTPPasswordMgrWithDefaultRealm, - # HTTPRedirectHandler, - # HTTPSHandler, - # URLError, - # build_opener, - # install_opener, - # OpenerDirector, - # ProxyBasicAuthHandler, - # ProxyDigestAuthHandler, - # ProxyHandler, - # Request, - # UnknownHandler, - # urlopen, - # ) - - # from urlparse import ( - # urldefrag - # urljoin, - # urlparse, - # urlunparse, - # urlsplit, - # urlunsplit, - # parse_qs, - # parse_q" - # ) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/response.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/response.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/response.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/response.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from future import standard_library -from future.utils import PY3 - -if PY3: - from urllib.response import * -else: - __future_module__ = True - with standard_library.suspend_hooks(): - from urllib import (addbase, - addclosehook, - addinfo, - addinfourl) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/robotparser.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/robotparser.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/urllib/robotparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/urllib/robotparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from urllib.robotparser import * -else: - __future_module__ = True - from robotparser import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/winreg.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/winreg.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/winreg.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/winreg.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from winreg import * -else: - __future_module__ = True - from _winreg import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/client.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/client.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from xmlrpc.client import * -else: - from xmlrpclib import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/server.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/server.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/moves/xmlrpc/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from xmlrpc.server import * -else: - from xmlrpclib import * diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/standard_library/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/standard_library/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/standard_library/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/standard_library/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,814 +0,0 @@ -""" -Python 3 reorganized the standard library (PEP 3108). This module exposes -several standard library modules to Python 2 under their new Python 3 -names. - -It is designed to be used as follows:: - - from future import standard_library - standard_library.install_aliases() - -And then these normal Py3 imports work on both Py3 and Py2:: - - import builtins - import copyreg - import queue - import reprlib - import socketserver - import winreg # on Windows only - import test.support - import html, html.parser, html.entites - import http, http.client, http.server - import http.cookies, http.cookiejar - import urllib.parse, urllib.request, urllib.response, urllib.error, urllib.robotparser - import xmlrpc.client, xmlrpc.server - - import _thread - import _dummy_thread - import _markupbase - - from itertools import filterfalse, zip_longest - from sys import intern - from collections import UserDict, UserList, UserString - from collections import OrderedDict, Counter, ChainMap # even on Py2.6 - from subprocess import getoutput, getstatusoutput - from subprocess import check_output # even on Py2.6 - -(The renamed modules and functions are still available under their old -names on Python 2.) - -This is a cleaner alternative to this idiom (see -http://docs.pythonsprints.com/python3_porting/py-porting.html):: - - try: - import queue - except ImportError: - import Queue as queue - - -Limitations ------------ -We don't currently support these modules, but would like to:: - - import dbm - import dbm.dumb - import dbm.gnu - import collections.abc # on Py33 - import pickle # should (optionally) bring in cPickle on Python 2 - -""" - -from __future__ import absolute_import, division, print_function - -import sys -import logging -import imp -import contextlib -import types -import copy -import os - -# Make a dedicated logger; leave the root logger to be configured -# by the application. -flog = logging.getLogger('future_stdlib') -_formatter = logging.Formatter(logging.BASIC_FORMAT) -_handler = logging.StreamHandler() -_handler.setFormatter(_formatter) -flog.addHandler(_handler) -flog.setLevel(logging.WARN) - -from future.utils import PY2, PY3 - -# The modules that are defined under the same names on Py3 but with -# different contents in a significant way (e.g. submodules) are: -# pickle (fast one) -# dbm -# urllib -# test -# email - -REPLACED_MODULES = set(['test', 'urllib', 'pickle', 'dbm']) # add email and dbm when we support it - -# The following module names are not present in Python 2.x, so they cause no -# potential clashes between the old and new names: -# http -# html -# tkinter -# xmlrpc -# Keys: Py2 / real module names -# Values: Py3 / simulated module names -RENAMES = { - # 'cStringIO': 'io', # there's a new io module in Python 2.6 - # that provides StringIO and BytesIO - # 'StringIO': 'io', # ditto - # 'cPickle': 'pickle', - '__builtin__': 'builtins', - 'copy_reg': 'copyreg', - 'Queue': 'queue', - 'future.moves.socketserver': 'socketserver', - 'ConfigParser': 'configparser', - 'repr': 'reprlib', - # 'FileDialog': 'tkinter.filedialog', - # 'tkFileDialog': 'tkinter.filedialog', - # 'SimpleDialog': 'tkinter.simpledialog', - # 'tkSimpleDialog': 'tkinter.simpledialog', - # 'tkColorChooser': 'tkinter.colorchooser', - # 'tkCommonDialog': 'tkinter.commondialog', - # 'Dialog': 'tkinter.dialog', - # 'Tkdnd': 'tkinter.dnd', - # 'tkFont': 'tkinter.font', - # 'tkMessageBox': 'tkinter.messagebox', - # 'ScrolledText': 'tkinter.scrolledtext', - # 'Tkconstants': 'tkinter.constants', - # 'Tix': 'tkinter.tix', - # 'ttk': 'tkinter.ttk', - # 'Tkinter': 'tkinter', - '_winreg': 'winreg', - 'thread': '_thread', - 'dummy_thread': '_dummy_thread', - # 'anydbm': 'dbm', # causes infinite import loop - # 'whichdb': 'dbm', # causes infinite import loop - # anydbm and whichdb are handled by fix_imports2 - # 'dbhash': 'dbm.bsd', - # 'dumbdbm': 'dbm.dumb', - # 'dbm': 'dbm.ndbm', - # 'gdbm': 'dbm.gnu', - 'future.moves.xmlrpc': 'xmlrpc', - # 'future.backports.email': 'email', # for use by urllib - # 'DocXMLRPCServer': 'xmlrpc.server', - # 'SimpleXMLRPCServer': 'xmlrpc.server', - # 'httplib': 'http.client', - # 'htmlentitydefs' : 'html.entities', - # 'HTMLParser' : 'html.parser', - # 'Cookie': 'http.cookies', - # 'cookielib': 'http.cookiejar', - # 'BaseHTTPServer': 'http.server', - # 'SimpleHTTPServer': 'http.server', - # 'CGIHTTPServer': 'http.server', - # 'future.backports.test': 'test', # primarily for renaming test_support to support - # 'commands': 'subprocess', - # 'urlparse' : 'urllib.parse', - # 'robotparser' : 'urllib.robotparser', - # 'abc': 'collections.abc', # for Py33 - # 'future.utils.six.moves.html': 'html', - # 'future.utils.six.moves.http': 'http', - 'future.moves.html': 'html', - 'future.moves.http': 'http', - # 'future.backports.urllib': 'urllib', - # 'future.utils.six.moves.urllib': 'urllib', - 'future.moves._markupbase': '_markupbase', - } - - -# It is complicated and apparently brittle to mess around with the -# ``sys.modules`` cache in order to support "import urllib" meaning two -# different things (Py2.7 urllib and backported Py3.3-like urllib) in different -# contexts. So we require explicit imports for these modules. -assert len(set(RENAMES.values()) & set(REPLACED_MODULES)) == 0 - - -# Harmless renames that we can insert. -# These modules need names from elsewhere being added to them: -# subprocess: should provide getoutput and other fns from commands -# module but these fns are missing: getstatus, mk2arg, -# mkarg -# re: needs an ASCII constant that works compatibly with Py3 - -# etc: see lib2to3/fixes/fix_imports.py - -# (New module name, new object name, old module name, old object name) -MOVES = [('collections', 'UserList', 'UserList', 'UserList'), - ('collections', 'UserDict', 'UserDict', 'UserDict'), - ('collections', 'UserString','UserString', 'UserString'), - ('itertools', 'filterfalse','itertools', 'ifilterfalse'), - ('itertools', 'zip_longest','itertools', 'izip_longest'), - ('sys', 'intern','__builtin__', 'intern'), - # The re module has no ASCII flag in Py2, but this is the default. - # Set re.ASCII to a zero constant. stat.ST_MODE just happens to be one - # (and it exists on Py2.6+). - ('re', 'ASCII','stat', 'ST_MODE'), - ('base64', 'encodebytes','base64', 'encodestring'), - ('base64', 'decodebytes','base64', 'decodestring'), - ('subprocess', 'getoutput', 'commands', 'getoutput'), - ('subprocess', 'getstatusoutput', 'commands', 'getstatusoutput'), - ('subprocess', 'check_output', 'future.backports.misc', 'check_output'), - ('math', 'ceil', 'future.backports.misc', 'ceil'), - ('collections', 'OrderedDict', 'future.backports.misc', 'OrderedDict'), - ('collections', 'Counter', 'future.backports.misc', 'Counter'), - ('collections', 'ChainMap', 'future.backports.misc', 'ChainMap'), - ('itertools', 'count', 'future.backports.misc', 'count'), - ('reprlib', 'recursive_repr', 'future.backports.misc', 'recursive_repr'), - ('functools', 'cmp_to_key', 'future.backports.misc', 'cmp_to_key'), - -# This is no use, since "import urllib.request" etc. still fails: -# ('urllib', 'error', 'future.moves.urllib', 'error'), -# ('urllib', 'parse', 'future.moves.urllib', 'parse'), -# ('urllib', 'request', 'future.moves.urllib', 'request'), -# ('urllib', 'response', 'future.moves.urllib', 'response'), -# ('urllib', 'robotparser', 'future.moves.urllib', 'robotparser'), - ] - - -# A minimal example of an import hook: -# class WarnOnImport(object): -# def __init__(self, *args): -# self.module_names = args -# -# def find_module(self, fullname, path=None): -# if fullname in self.module_names: -# self.path = path -# return self -# return None -# -# def load_module(self, name): -# if name in sys.modules: -# return sys.modules[name] -# module_info = imp.find_module(name, self.path) -# module = imp.load_module(name, *module_info) -# sys.modules[name] = module -# flog.warning("Imported deprecated module %s", name) -# return module - - -class RenameImport(object): - """ - A class for import hooks mapping Py3 module names etc. to the Py2 equivalents. - """ - # Different RenameImport classes are created when importing this module from - # different source files. This causes isinstance(hook, RenameImport) checks - # to produce inconsistent results. We add this RENAMER attribute here so - # remove_hooks() and install_hooks() can find instances of these classes - # easily: - RENAMER = True - - def __init__(self, old_to_new): - ''' - Pass in a dictionary-like object mapping from old names to new - names. E.g. {'ConfigParser': 'configparser', 'cPickle': 'pickle'} - ''' - self.old_to_new = old_to_new - both = set(old_to_new.keys()) & set(old_to_new.values()) - assert (len(both) == 0 and - len(set(old_to_new.values())) == len(old_to_new.values())), \ - 'Ambiguity in renaming (handler not implemented)' - self.new_to_old = dict((new, old) for (old, new) in old_to_new.items()) - - def find_module(self, fullname, path=None): - # Handles hierarchical importing: package.module.module2 - new_base_names = set([s.split('.')[0] for s in self.new_to_old]) - # Before v0.12: Was: if fullname in set(self.old_to_new) | new_base_names: - if fullname in new_base_names: - return self - return None - - def load_module(self, name): - path = None - if name in sys.modules: - return sys.modules[name] - elif name in self.new_to_old: - # New name. Look up the corresponding old (Py2) name: - oldname = self.new_to_old[name] - module = self._find_and_load_module(oldname) - # module.__future_module__ = True - else: - module = self._find_and_load_module(name) - # In any case, make it available under the requested (Py3) name - sys.modules[name] = module - return module - - def _find_and_load_module(self, name, path=None): - """ - Finds and loads it. But if there's a . in the name, handles it - properly. - """ - bits = name.split('.') - while len(bits) > 1: - # Treat the first bit as a package - packagename = bits.pop(0) - package = self._find_and_load_module(packagename, path) - try: - path = package.__path__ - except AttributeError: - # This could be e.g. moves. - flog.debug('Package {0} has no __path__.'.format(package)) - if name in sys.modules: - return sys.modules[name] - flog.debug('What to do here?') - - name = bits[0] - module_info = imp.find_module(name, path) - return imp.load_module(name, *module_info) - - -class hooks(object): - """ - Acts as a context manager. Saves the state of sys.modules and restores it - after the 'with' block. - - Use like this: - - >>> from future import standard_library - >>> with standard_library.hooks(): - ... import http.client - >>> import requests - - For this to work, http.client will be scrubbed from sys.modules after the - 'with' block. That way the modules imported in the 'with' block will - continue to be accessible in the current namespace but not from any - imported modules (like requests). - """ - def __enter__(self): - # flog.debug('Entering hooks context manager') - self.old_sys_modules = copy.copy(sys.modules) - self.hooks_were_installed = detect_hooks() - # self.scrubbed = scrub_py2_sys_modules() - install_hooks() - return self - - def __exit__(self, *args): - # flog.debug('Exiting hooks context manager') - # restore_sys_modules(self.scrubbed) - if not self.hooks_were_installed: - remove_hooks() - # scrub_future_sys_modules() - -# Sanity check for is_py2_stdlib_module(): We aren't replacing any -# builtin modules names: -if PY2: - assert len(set(RENAMES.values()) & set(sys.builtin_module_names)) == 0 - - -def is_py2_stdlib_module(m): - """ - Tries to infer whether the module m is from the Python 2 standard library. - This may not be reliable on all systems. - """ - if PY3: - return False - if not 'stdlib_path' in is_py2_stdlib_module.__dict__: - stdlib_files = [contextlib.__file__, os.__file__, copy.__file__] - stdlib_paths = [os.path.split(f)[0] for f in stdlib_files] - if not len(set(stdlib_paths)) == 1: - # This seems to happen on travis-ci.org. Very strange. We'll try to - # ignore it. - flog.warn('Multiple locations found for the Python standard ' - 'library: %s' % stdlib_paths) - # Choose the first one arbitrarily - is_py2_stdlib_module.stdlib_path = stdlib_paths[0] - - if m.__name__ in sys.builtin_module_names: - return True - - if hasattr(m, '__file__'): - modpath = os.path.split(m.__file__) - if (modpath[0].startswith(is_py2_stdlib_module.stdlib_path) and - 'site-packages' not in modpath[0]): - return True - - return False - - -def scrub_py2_sys_modules(): - """ - Removes any Python 2 standard library modules from ``sys.modules`` that - would interfere with Py3-style imports using import hooks. Examples are - modules with the same names (like urllib or email). - - (Note that currently import hooks are disabled for modules like these - with ambiguous names anyway ...) - """ - if PY3: - return {} - scrubbed = {} - for modulename in REPLACED_MODULES & set(RENAMES.keys()): - if not modulename in sys.modules: - continue - - module = sys.modules[modulename] - - if is_py2_stdlib_module(module): - flog.debug('Deleting (Py2) {} from sys.modules'.format(modulename)) - scrubbed[modulename] = sys.modules[modulename] - del sys.modules[modulename] - return scrubbed - - -def scrub_future_sys_modules(): - """ - Deprecated. - """ - return {} - -class suspend_hooks(object): - """ - Acts as a context manager. Use like this: - - >>> from future import standard_library - >>> standard_library.install_hooks() - >>> import http.client - >>> # ... - >>> with standard_library.suspend_hooks(): - >>> import requests # incompatible with ``future``'s standard library hooks - - If the hooks were disabled before the context, they are not installed when - the context is left. - """ - def __enter__(self): - self.hooks_were_installed = detect_hooks() - remove_hooks() - # self.scrubbed = scrub_future_sys_modules() - return self - - def __exit__(self, *args): - if self.hooks_were_installed: - install_hooks() - # restore_sys_modules(self.scrubbed) - - -def restore_sys_modules(scrubbed): - """ - Add any previously scrubbed modules back to the sys.modules cache, - but only if it's safe to do so. - """ - clash = set(sys.modules) & set(scrubbed) - if len(clash) != 0: - # If several, choose one arbitrarily to raise an exception about - first = list(clash)[0] - raise ImportError('future module {} clashes with Py2 module' - .format(first)) - sys.modules.update(scrubbed) - - -def install_aliases(): - """ - Monkey-patches the standard library in Py2.6/7 to provide - aliases for better Py3 compatibility. - """ - if PY3: - return - # if hasattr(install_aliases, 'run_already'): - # return - for (newmodname, newobjname, oldmodname, oldobjname) in MOVES: - __import__(newmodname) - # We look up the module in sys.modules because __import__ just returns the - # top-level package: - newmod = sys.modules[newmodname] - # newmod.__future_module__ = True - - __import__(oldmodname) - oldmod = sys.modules[oldmodname] - - obj = getattr(oldmod, oldobjname) - setattr(newmod, newobjname, obj) - - # Hack for urllib so it appears to have the same structure on Py2 as on Py3 - import urllib - from future.backports.urllib import request - from future.backports.urllib import response - from future.backports.urllib import parse - from future.backports.urllib import error - from future.backports.urllib import robotparser - urllib.request = request - urllib.response = response - urllib.parse = parse - urllib.error = error - urllib.robotparser = robotparser - sys.modules['urllib.request'] = request - sys.modules['urllib.response'] = response - sys.modules['urllib.parse'] = parse - sys.modules['urllib.error'] = error - sys.modules['urllib.robotparser'] = robotparser - - # Patch the test module so it appears to have the same structure on Py2 as on Py3 - try: - import test - except ImportError: - pass - try: - from future.moves.test import support - except ImportError: - pass - else: - test.support = support - sys.modules['test.support'] = support - - # Patch the dbm module so it appears to have the same structure on Py2 as on Py3 - try: - import dbm - except ImportError: - pass - else: - from future.moves.dbm import dumb - dbm.dumb = dumb - sys.modules['dbm.dumb'] = dumb - try: - from future.moves.dbm import gnu - except ImportError: - pass - else: - dbm.gnu = gnu - sys.modules['dbm.gnu'] = gnu - try: - from future.moves.dbm import ndbm - except ImportError: - pass - else: - dbm.ndbm = ndbm - sys.modules['dbm.ndbm'] = ndbm - - # install_aliases.run_already = True - - -def install_hooks(): - """ - This function installs the future.standard_library import hook into - sys.meta_path. - """ - if PY3: - return - - install_aliases() - - flog.debug('sys.meta_path was: {0}'.format(sys.meta_path)) - flog.debug('Installing hooks ...') - - # Add it unless it's there already - newhook = RenameImport(RENAMES) - if not detect_hooks(): - sys.meta_path.append(newhook) - flog.debug('sys.meta_path is now: {0}'.format(sys.meta_path)) - - -def enable_hooks(): - """ - Deprecated. Use install_hooks() instead. This will be removed by - ``future`` v1.0. - """ - install_hooks() - - -def remove_hooks(scrub_sys_modules=False): - """ - This function removes the import hook from sys.meta_path. - """ - if PY3: - return - flog.debug('Uninstalling hooks ...') - # Loop backwards, so deleting items keeps the ordering: - for i, hook in list(enumerate(sys.meta_path))[::-1]: - if hasattr(hook, 'RENAMER'): - del sys.meta_path[i] - - # Explicit is better than implicit. In the future the interface should - # probably change so that scrubbing the import hooks requires a separate - # function call. Left as is for now for backward compatibility with - # v0.11.x. - if scrub_sys_modules: - scrub_future_sys_modules() - - -def disable_hooks(): - """ - Deprecated. Use remove_hooks() instead. This will be removed by - ``future`` v1.0. - """ - remove_hooks() - - -def detect_hooks(): - """ - Returns True if the import hooks are installed, False if not. - """ - flog.debug('Detecting hooks ...') - present = any([hasattr(hook, 'RENAMER') for hook in sys.meta_path]) - if present: - flog.debug('Detected.') - else: - flog.debug('Not detected.') - return present - - -# As of v0.12, this no longer happens implicitly: -# if not PY3: -# install_hooks() - - -if not hasattr(sys, 'py2_modules'): - sys.py2_modules = {} - -def cache_py2_modules(): - """ - Currently this function is unneeded, as we are not attempting to provide import hooks - for modules with ambiguous names: email, urllib, pickle. - """ - if len(sys.py2_modules) != 0: - return - assert not detect_hooks() - import urllib - sys.py2_modules['urllib'] = urllib - - import email - sys.py2_modules['email'] = email - - import pickle - sys.py2_modules['pickle'] = pickle - - # Not all Python installations have test module. (Anaconda doesn't, for example.) - # try: - # import test - # except ImportError: - # sys.py2_modules['test'] = None - # sys.py2_modules['test'] = test - - # import dbm - # sys.py2_modules['dbm'] = dbm - - -def import_(module_name, backport=False): - """ - Pass a (potentially dotted) module name of a Python 3 standard library - module. This function imports the module compatibly on Py2 and Py3 and - returns the top-level module. - - Example use: - >>> http = import_('http.client') - >>> http = import_('http.server') - >>> urllib = import_('urllib.request') - - Then: - >>> conn = http.client.HTTPConnection(...) - >>> response = urllib.request.urlopen('http://mywebsite.com') - >>> # etc. - - Use as follows: - >>> package_name = import_(module_name) - - On Py3, equivalent to this: - - >>> import module_name - - On Py2, equivalent to this if backport=False: - - >>> from future.moves import module_name - - or to this if backport=True: - - >>> from future.backports import module_name - - except that it also handles dotted module names such as ``http.client`` - The effect then is like this: - - >>> from future.backports import module - >>> from future.backports.module import submodule - >>> module.submodule = submodule - - Note that this would be a SyntaxError in Python: - - >>> from future.backports import http.client - - """ - # Python 2.6 doesn't have importlib in the stdlib, so it requires - # the backported ``importlib`` package from PyPI as a dependency to use - # this function: - import importlib - - if PY3: - return __import__(module_name) - else: - # client.blah = blah - # Then http.client = client - # etc. - if backport: - prefix = 'future.backports' - else: - prefix = 'future.moves' - parts = prefix.split('.') + module_name.split('.') - - modules = [] - for i, part in enumerate(parts): - sofar = '.'.join(parts[:i+1]) - modules.append(importlib.import_module(sofar)) - for i, part in reversed(list(enumerate(parts))): - if i == 0: - break - setattr(modules[i-1], part, modules[i]) - - # Return the next-most top-level module after future.backports / future.moves: - return modules[2] - - -def from_import(module_name, *symbol_names, **kwargs): - """ - Example use: - >>> HTTPConnection = from_import('http.client', 'HTTPConnection') - >>> HTTPServer = from_import('http.server', 'HTTPServer') - >>> urlopen, urlparse = from_import('urllib.request', 'urlopen', 'urlparse') - - Equivalent to this on Py3: - - >>> from module_name import symbol_names[0], symbol_names[1], ... - - and this on Py2: - - >>> from future.moves.module_name import symbol_names[0], ... - - or: - - >>> from future.backports.module_name import symbol_names[0], ... - - except that it also handles dotted module names such as ``http.client``. - """ - - if PY3: - return __import__(module_name) - else: - if 'backport' in kwargs and bool(kwargs['backport']): - prefix = 'future.backports' - else: - prefix = 'future.moves' - parts = prefix.split('.') + module_name.split('.') - module = importlib.import_module(prefix + '.' + module_name) - output = [getattr(module, name) for name in symbol_names] - if len(output) == 1: - return output[0] - else: - return output - - -class exclude_local_folder_imports(object): - """ - A context-manager that prevents standard library modules like configparser - from being imported from the local python-future source folder on Py3. - - (This was need prior to v0.16.0 because the presence of a configparser - folder would otherwise have prevented setuptools from running on Py3. Maybe - it's not needed any more?) - """ - def __init__(self, *args): - assert len(args) > 0 - self.module_names = args - # Disallow dotted module names like http.client: - if any(['.' in m for m in self.module_names]): - raise NotImplementedError('Dotted module names are not supported') - - def __enter__(self): - self.old_sys_path = copy.copy(sys.path) - self.old_sys_modules = copy.copy(sys.modules) - if sys.version_info[0] < 3: - return - # The presence of all these indicates we've found our source folder, - # because `builtins` won't have been installed in site-packages by setup.py: - FUTURE_SOURCE_SUBFOLDERS = ['future', 'past', 'libfuturize', 'libpasteurize', 'builtins'] - - # Look for the future source folder: - for folder in self.old_sys_path: - if all([os.path.exists(os.path.join(folder, subfolder)) - for subfolder in FUTURE_SOURCE_SUBFOLDERS]): - # Found it. Remove it. - sys.path.remove(folder) - - # Ensure we import the system module: - for m in self.module_names: - # Delete the module and any submodules from sys.modules: - # for key in list(sys.modules): - # if key == m or key.startswith(m + '.'): - # try: - # del sys.modules[key] - # except KeyError: - # pass - try: - module = __import__(m, level=0) - except ImportError: - # There's a problem importing the system module. E.g. the - # winreg module is not available except on Windows. - pass - - def __exit__(self, *args): - # Restore sys.path and sys.modules: - sys.path = self.old_sys_path - for m in set(self.old_sys_modules.keys()) - set(sys.modules.keys()): - sys.modules[m] = self.old_sys_modules[m] - -TOP_LEVEL_MODULES = ['builtins', - 'copyreg', - 'html', - 'http', - 'queue', - 'reprlib', - 'socketserver', - 'test', - 'tkinter', - 'winreg', - 'xmlrpc', - '_dummy_thread', - '_markupbase', - '_thread', - ] - -def import_top_level_modules(): - with exclude_local_folder_imports(*TOP_LEVEL_MODULES): - for m in TOP_LEVEL_MODULES: - try: - __import__(m) - except ImportError: # e.g. winreg - pass diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/tests/base.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/tests/base.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/tests/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/tests/base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,531 +0,0 @@ -from __future__ import print_function, absolute_import -import os -import tempfile -import unittest -import sys -import re -import warnings -import io -from textwrap import dedent - -from future.utils import bind_method, PY26, PY3, PY2, PY27 -from future.moves.subprocess import check_output, STDOUT, CalledProcessError - -if PY26: - import unittest2 as unittest - - -def reformat_code(code): - """ - Removes any leading \n and dedents. - """ - if code.startswith('\n'): - code = code[1:] - return dedent(code) - - -def order_future_lines(code): - """ - Returns the code block with any ``__future__`` import lines sorted, and - then any ``future`` import lines sorted, then any ``builtins`` import lines - sorted. - - This only sorts the lines within the expected blocks. - - See test_order_future_lines() for an example. - """ - - # We need .splitlines(keepends=True), which doesn't exist on Py2, - # so we use this instead: - lines = code.split('\n') - - uufuture_line_numbers = [i for i, line in enumerate(lines) - if line.startswith('from __future__ import ')] - - future_line_numbers = [i for i, line in enumerate(lines) - if line.startswith('from future') - or line.startswith('from past')] - - builtins_line_numbers = [i for i, line in enumerate(lines) - if line.startswith('from builtins')] - - assert code.lstrip() == code, ('internal usage error: ' - 'dedent the code before calling order_future_lines()') - - def mymax(numbers): - return max(numbers) if len(numbers) > 0 else 0 - - def mymin(numbers): - return min(numbers) if len(numbers) > 0 else float('inf') - - assert mymax(uufuture_line_numbers) <= mymin(future_line_numbers), \ - 'the __future__ and future imports are out of order' - - # assert mymax(future_line_numbers) <= mymin(builtins_line_numbers), \ - # 'the future and builtins imports are out of order' - - uul = sorted([lines[i] for i in uufuture_line_numbers]) - sorted_uufuture_lines = dict(zip(uufuture_line_numbers, uul)) - - fl = sorted([lines[i] for i in future_line_numbers]) - sorted_future_lines = dict(zip(future_line_numbers, fl)) - - bl = sorted([lines[i] for i in builtins_line_numbers]) - sorted_builtins_lines = dict(zip(builtins_line_numbers, bl)) - - # Replace the old unsorted "from __future__ import ..." lines with the - # new sorted ones: - new_lines = [] - for i in range(len(lines)): - if i in uufuture_line_numbers: - new_lines.append(sorted_uufuture_lines[i]) - elif i in future_line_numbers: - new_lines.append(sorted_future_lines[i]) - elif i in builtins_line_numbers: - new_lines.append(sorted_builtins_lines[i]) - else: - new_lines.append(lines[i]) - return '\n'.join(new_lines) - - -class VerboseCalledProcessError(CalledProcessError): - """ - Like CalledProcessError, but it displays more information (message and - script output) for diagnosing test failures etc. - """ - def __init__(self, msg, returncode, cmd, output=None): - self.msg = msg - self.returncode = returncode - self.cmd = cmd - self.output = output - - def __str__(self): - return ("Command '%s' failed with exit status %d\nMessage: %s\nOutput: %s" - % (self.cmd, self.returncode, self.msg, self.output)) - -class FuturizeError(VerboseCalledProcessError): - pass - -class PasteurizeError(VerboseCalledProcessError): - pass - - -class CodeHandler(unittest.TestCase): - """ - Handy mixin for test classes for writing / reading / futurizing / - running .py files in the test suite. - """ - def setUp(self): - """ - The outputs from the various futurize stages should have the - following headers: - """ - # After stage1: - # TODO: use this form after implementing a fixer to consolidate - # __future__ imports into a single line: - # self.headers1 = """ - # from __future__ import absolute_import, division, print_function - # """ - self.headers1 = reformat_code(""" - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - """) - - # After stage2 --all-imports: - # TODO: use this form after implementing a fixer to consolidate - # __future__ imports into a single line: - # self.headers2 = """ - # from __future__ import (absolute_import, division, - # print_function, unicode_literals) - # from future import standard_library - # from future.builtins import * - # """ - self.headers2 = reformat_code(""" - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - from __future__ import unicode_literals - from future import standard_library - standard_library.install_aliases() - from builtins import * - """) - self.interpreters = [sys.executable] - self.tempdir = tempfile.mkdtemp() + os.path.sep - pypath = os.getenv('PYTHONPATH') - if pypath: - self.env = {'PYTHONPATH': os.getcwd() + os.pathsep + pypath} - else: - self.env = {'PYTHONPATH': os.getcwd()} - - def convert(self, code, stages=(1, 2), all_imports=False, from3=False, - reformat=True, run=True, conservative=False): - """ - Converts the code block using ``futurize`` and returns the - resulting code. - - Passing stages=[1] or stages=[2] passes the flag ``--stage1`` or - ``stage2`` to ``futurize``. Passing both stages runs ``futurize`` - with both stages by default. - - If from3 is False, runs ``futurize``, converting from Python 2 to - both 2 and 3. If from3 is True, runs ``pasteurize`` to convert - from Python 3 to both 2 and 3. - - Optionally reformats the code block first using the reformat() function. - - If run is True, runs the resulting code under all Python - interpreters in self.interpreters. - """ - if reformat: - code = reformat_code(code) - self._write_test_script(code) - self._futurize_test_script(stages=stages, all_imports=all_imports, - from3=from3, conservative=conservative) - output = self._read_test_script() - if run: - for interpreter in self.interpreters: - _ = self._run_test_script(interpreter=interpreter) - return output - - def compare(self, output, expected, ignore_imports=True): - """ - Compares whether the code blocks are equal. If not, raises an - exception so the test fails. Ignores any trailing whitespace like - blank lines. - - If ignore_imports is True, passes the code blocks into the - strip_future_imports method. - - If one code block is a unicode string and the other a - byte-string, it assumes the byte-string is encoded as utf-8. - """ - if ignore_imports: - output = self.strip_future_imports(output) - expected = self.strip_future_imports(expected) - if isinstance(output, bytes) and not isinstance(expected, bytes): - output = output.decode('utf-8') - if isinstance(expected, bytes) and not isinstance(output, bytes): - expected = expected.decode('utf-8') - self.assertEqual(order_future_lines(output.rstrip()), - expected.rstrip()) - - def strip_future_imports(self, code): - """ - Strips any of these import lines: - - from __future__ import - from future - from future. - from builtins - - or any line containing: - install_hooks() - or: - install_aliases() - - Limitation: doesn't handle imports split across multiple lines like - this: - - from __future__ import (absolute_import, division, print_function, - unicode_literals) - """ - output = [] - # We need .splitlines(keepends=True), which doesn't exist on Py2, - # so we use this instead: - for line in code.split('\n'): - if not (line.startswith('from __future__ import ') - or line.startswith('from future ') - or line.startswith('from builtins ') - or 'install_hooks()' in line - or 'install_aliases()' in line - # but don't match "from future_builtins" :) - or line.startswith('from future.')): - output.append(line) - return '\n'.join(output) - - def convert_check(self, before, expected, stages=(1, 2), all_imports=False, - ignore_imports=True, from3=False, run=True, - conservative=False): - """ - Convenience method that calls convert() and compare(). - - Reformats the code blocks automatically using the reformat_code() - function. - - If all_imports is passed, we add the appropriate import headers - for the stage(s) selected to the ``expected`` code-block, so they - needn't appear repeatedly in the test code. - - If ignore_imports is True, ignores the presence of any lines - beginning: - - from __future__ import ... - from future import ... - - for the purpose of the comparison. - """ - output = self.convert(before, stages=stages, all_imports=all_imports, - from3=from3, run=run, conservative=conservative) - if all_imports: - headers = self.headers2 if 2 in stages else self.headers1 - else: - headers = '' - - self.compare(output, headers + reformat_code(expected), - ignore_imports=ignore_imports) - - def unchanged(self, code, **kwargs): - """ - Convenience method to ensure the code is unchanged by the - futurize process. - """ - self.convert_check(code, code, **kwargs) - - def _write_test_script(self, code, filename='mytestscript.py'): - """ - Dedents the given code (a multiline string) and writes it out to - a file in a temporary folder like /tmp/tmpUDCn7x/mytestscript.py. - """ - if isinstance(code, bytes): - code = code.decode('utf-8') - # Be explicit about encoding the temp file as UTF-8 (issue #63): - with io.open(self.tempdir + filename, 'wt', encoding='utf-8') as f: - f.write(dedent(code)) - - def _read_test_script(self, filename='mytestscript.py'): - with io.open(self.tempdir + filename, 'rt', encoding='utf-8') as f: - newsource = f.read() - return newsource - - def _futurize_test_script(self, filename='mytestscript.py', stages=(1, 2), - all_imports=False, from3=False, - conservative=False): - params = [] - stages = list(stages) - if all_imports: - params.append('--all-imports') - if from3: - script = 'pasteurize.py' - else: - script = 'futurize.py' - if stages == [1]: - params.append('--stage1') - elif stages == [2]: - params.append('--stage2') - else: - assert stages == [1, 2] - if conservative: - params.append('--conservative') - # No extra params needed - - # Absolute file path: - fn = self.tempdir + filename - call_args = [sys.executable, script] + params + ['-w', fn] - try: - output = check_output(call_args, stderr=STDOUT, env=self.env) - except CalledProcessError as e: - with open(fn) as f: - msg = ( - 'Error running the command %s\n' - '%s\n' - 'Contents of file %s:\n' - '\n' - '%s') % ( - ' '.join(call_args), - 'env=%s' % self.env, - fn, - '----\n%s\n----' % f.read(), - ) - ErrorClass = (FuturizeError if 'futurize' in script else PasteurizeError) - raise ErrorClass(msg, e.returncode, e.cmd, output=e.output) - return output - - def _run_test_script(self, filename='mytestscript.py', - interpreter=sys.executable): - # Absolute file path: - fn = self.tempdir + filename - try: - output = check_output([interpreter, fn], - env=self.env, stderr=STDOUT) - except CalledProcessError as e: - with open(fn) as f: - msg = ( - 'Error running the command %s\n' - '%s\n' - 'Contents of file %s:\n' - '\n' - '%s') % ( - ' '.join([interpreter, fn]), - 'env=%s' % self.env, - fn, - '----\n%s\n----' % f.read(), - ) - if not hasattr(e, 'output'): - # The attribute CalledProcessError.output doesn't exist on Py2.6 - e.output = None - raise VerboseCalledProcessError(msg, e.returncode, e.cmd, output=e.output) - return output - - -# Decorator to skip some tests on Python 2.6 ... -skip26 = unittest.skipIf(PY26, "this test is known to fail on Py2.6") - - -def expectedFailurePY3(func): - if not PY3: - return func - return unittest.expectedFailure(func) - -def expectedFailurePY26(func): - if not PY26: - return func - return unittest.expectedFailure(func) - - -def expectedFailurePY27(func): - if not PY27: - return func - return unittest.expectedFailure(func) - - -def expectedFailurePY2(func): - if not PY2: - return func - return unittest.expectedFailure(func) - - -# Renamed in Py3.3: -if not hasattr(unittest.TestCase, 'assertRaisesRegex'): - unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp - -# From Py3.3: -def assertRegex(self, text, expected_regex, msg=None): - """Fail the test unless the text matches the regular expression.""" - if isinstance(expected_regex, (str, unicode)): - assert expected_regex, "expected_regex must not be empty." - expected_regex = re.compile(expected_regex) - if not expected_regex.search(text): - msg = msg or "Regex didn't match" - msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text) - raise self.failureException(msg) - -if not hasattr(unittest.TestCase, 'assertRegex'): - bind_method(unittest.TestCase, 'assertRegex', assertRegex) - -class _AssertRaisesBaseContext(object): - - def __init__(self, expected, test_case, callable_obj=None, - expected_regex=None): - self.expected = expected - self.test_case = test_case - if callable_obj is not None: - try: - self.obj_name = callable_obj.__name__ - except AttributeError: - self.obj_name = str(callable_obj) - else: - self.obj_name = None - if isinstance(expected_regex, (bytes, str)): - expected_regex = re.compile(expected_regex) - self.expected_regex = expected_regex - self.msg = None - - def _raiseFailure(self, standardMsg): - msg = self.test_case._formatMessage(self.msg, standardMsg) - raise self.test_case.failureException(msg) - - def handle(self, name, callable_obj, args, kwargs): - """ - If callable_obj is None, assertRaises/Warns is being used as a - context manager, so check for a 'msg' kwarg and return self. - If callable_obj is not None, call it passing args and kwargs. - """ - if callable_obj is None: - self.msg = kwargs.pop('msg', None) - return self - with self: - callable_obj(*args, **kwargs) - -class _AssertWarnsContext(_AssertRaisesBaseContext): - """A context manager used to implement TestCase.assertWarns* methods.""" - - def __enter__(self): - # The __warningregistry__'s need to be in a pristine state for tests - # to work properly. - for v in sys.modules.values(): - if getattr(v, '__warningregistry__', None): - v.__warningregistry__ = {} - self.warnings_manager = warnings.catch_warnings(record=True) - self.warnings = self.warnings_manager.__enter__() - warnings.simplefilter("always", self.expected) - return self - - def __exit__(self, exc_type, exc_value, tb): - self.warnings_manager.__exit__(exc_type, exc_value, tb) - if exc_type is not None: - # let unexpected exceptions pass through - return - try: - exc_name = self.expected.__name__ - except AttributeError: - exc_name = str(self.expected) - first_matching = None - for m in self.warnings: - w = m.message - if not isinstance(w, self.expected): - continue - if first_matching is None: - first_matching = w - if (self.expected_regex is not None and - not self.expected_regex.search(str(w))): - continue - # store warning for later retrieval - self.warning = w - self.filename = m.filename - self.lineno = m.lineno - return - # Now we simply try to choose a helpful failure message - if first_matching is not None: - self._raiseFailure('"{}" does not match "{}"'.format( - self.expected_regex.pattern, str(first_matching))) - if self.obj_name: - self._raiseFailure("{} not triggered by {}".format(exc_name, - self.obj_name)) - else: - self._raiseFailure("{} not triggered".format(exc_name)) - - -def assertWarns(self, expected_warning, callable_obj=None, *args, **kwargs): - """Fail unless a warning of class warnClass is triggered - by callable_obj when invoked with arguments args and keyword - arguments kwargs. If a different type of warning is - triggered, it will not be handled: depending on the other - warning filtering rules in effect, it might be silenced, printed - out, or raised as an exception. - - If called with callable_obj omitted or None, will return a - context object used like this:: - - with self.assertWarns(SomeWarning): - do_something() - - An optional keyword argument 'msg' can be provided when assertWarns - is used as a context object. - - The context manager keeps a reference to the first matching - warning as the 'warning' attribute; similarly, the 'filename' - and 'lineno' attributes give you information about the line - of Python code from which the warning was triggered. - This allows you to inspect the warning after the assertion:: - - with self.assertWarns(SomeWarning) as cm: - do_something() - the_warning = cm.warning - self.assertEqual(the_warning.some_attribute, 147) - """ - context = _AssertWarnsContext(expected_warning, self, callable_obj) - return context.handle('assertWarns', callable_obj, args, kwargs) - -if not hasattr(unittest.TestCase, 'assertWarns'): - bind_method(unittest.TestCase, 'assertWarns', assertWarns) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,258 +0,0 @@ -""" -This module contains backports the data types that were significantly changed -in the transition from Python 2 to Python 3. - -- an implementation of Python 3's bytes object (pure Python subclass of - Python 2's builtin 8-bit str type) -- an implementation of Python 3's str object (pure Python subclass of - Python 2's builtin unicode type) -- a backport of the range iterator from Py3 with slicing support - -It is used as follows:: - - from __future__ import division, absolute_import, print_function - from builtins import bytes, dict, int, range, str - -to bring in the new semantics for these functions from Python 3. And -then, for example:: - - b = bytes(b'ABCD') - assert list(b) == [65, 66, 67, 68] - assert repr(b) == "b'ABCD'" - assert [65, 66] in b - - # These raise TypeErrors: - # b + u'EFGH' - # b.split(u'B') - # bytes(b',').join([u'Fred', u'Bill']) - - - s = str(u'ABCD') - - # These raise TypeErrors: - # s.join([b'Fred', b'Bill']) - # s.startswith(b'A') - # b'B' in s - # s.find(b'A') - # s.replace(u'A', b'a') - - # This raises an AttributeError: - # s.decode('utf-8') - - assert repr(s) == 'ABCD' # consistent repr with Py3 (no u prefix) - - - for i in range(10**11)[:10]: - pass - -and:: - - class VerboseList(list): - def append(self, item): - print('Adding an item') - super().append(item) # new simpler super() function - -For more information: ---------------------- - -- future.types.newbytes -- future.types.newdict -- future.types.newint -- future.types.newobject -- future.types.newrange -- future.types.newstr - - -Notes -===== - -range() -------- -``range`` is a custom class that backports the slicing behaviour from -Python 3 (based on the ``xrange`` module by Dan Crosta). See the -``newrange`` module docstring for more details. - - -super() -------- -``super()`` is based on Ryan Kelly's ``magicsuper`` module. See the -``newsuper`` module docstring for more details. - - -round() -------- -Python 3 modifies the behaviour of ``round()`` to use "Banker's Rounding". -See http://stackoverflow.com/a/10825998. See the ``newround`` module -docstring for more details. - -""" - -from __future__ import absolute_import, division, print_function - -import functools -from numbers import Integral - -from future import utils - - -# Some utility functions to enforce strict type-separation of unicode str and -# bytes: -def disallow_types(argnums, disallowed_types): - """ - A decorator that raises a TypeError if any of the given numbered - arguments is of the corresponding given type (e.g. bytes or unicode - string). - - For example: - - @disallow_types([0, 1], [unicode, bytes]) - def f(a, b): - pass - - raises a TypeError when f is called if a unicode object is passed as - `a` or a bytes object is passed as `b`. - - This also skips over keyword arguments, so - - @disallow_types([0, 1], [unicode, bytes]) - def g(a, b=None): - pass - - doesn't raise an exception if g is called with only one argument a, - e.g.: - - g(b'Byte string') - - Example use: - - >>> class newbytes(object): - ... @disallow_types([1], [unicode]) - ... def __add__(self, other): - ... pass - - >>> newbytes('1234') + u'1234' #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - TypeError: can't concat 'bytes' to (unicode) str - """ - - def decorator(function): - - @functools.wraps(function) - def wrapper(*args, **kwargs): - # These imports are just for this decorator, and are defined here - # to prevent circular imports: - from .newbytes import newbytes - from .newint import newint - from .newstr import newstr - - errmsg = "argument can't be {0}" - for (argnum, mytype) in zip(argnums, disallowed_types): - # Handle the case where the type is passed as a string like 'newbytes'. - if isinstance(mytype, str) or isinstance(mytype, bytes): - mytype = locals()[mytype] - - # Only restrict kw args only if they are passed: - if len(args) <= argnum: - break - - # Here we use type() rather than isinstance() because - # __instancecheck__ is being overridden. E.g. - # isinstance(b'abc', newbytes) is True on Py2. - if type(args[argnum]) == mytype: - raise TypeError(errmsg.format(mytype)) - - return function(*args, **kwargs) - return wrapper - return decorator - - -def no(mytype, argnums=(1,)): - """ - A shortcut for the disallow_types decorator that disallows only one type - (in any position in argnums). - - Example use: - - >>> class newstr(object): - ... @no('bytes') - ... def __add__(self, other): - ... pass - - >>> newstr(u'1234') + b'1234' #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - TypeError: argument can't be bytes - - The object can also be passed directly, but passing the string helps - to prevent circular import problems. - """ - if isinstance(argnums, Integral): - argnums = (argnums,) - disallowed_types = [mytype] * len(argnums) - return disallow_types(argnums, disallowed_types) - - -def issubset(list1, list2): - """ - Examples: - - >>> issubset([], [65, 66, 67]) - True - >>> issubset([65], [65, 66, 67]) - True - >>> issubset([65, 66], [65, 66, 67]) - True - >>> issubset([65, 67], [65, 66, 67]) - False - """ - n = len(list1) - for startpos in range(len(list2) - n + 1): - if list2[startpos:startpos+n] == list1: - return True - return False - - -if utils.PY3: - import builtins - bytes = builtins.bytes - dict = builtins.dict - int = builtins.int - list = builtins.list - object = builtins.object - range = builtins.range - str = builtins.str - - # The identity mapping - newtypes = {bytes: bytes, - dict: dict, - int: int, - list: list, - object: object, - range: range, - str: str} - - __all__ = ['newtypes'] - -else: - - from .newbytes import newbytes - from .newdict import newdict - from .newint import newint - from .newlist import newlist - from .newrange import newrange - from .newobject import newobject - from .newstr import newstr - - newtypes = {bytes: newbytes, - dict: newdict, - int: newint, - long: newint, - list: newlist, - object: newobject, - range: newrange, - str: newbytes, - unicode: newstr} - - __all__ = ['newbytes', 'newdict', 'newint', 'newlist', 'newrange', 'newstr', 'newtypes'] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newbytes.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newbytes.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newbytes.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newbytes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,433 +0,0 @@ -""" -Pure-Python implementation of a Python 3-like bytes object for Python 2. - -Why do this? Without it, the Python 2 bytes object is a very, very -different beast to the Python 3 bytes object. -""" - -from collections import Iterable -from numbers import Integral -import string -import copy - -from future.utils import istext, isbytes, PY3, with_metaclass -from future.types import no, issubset -from future.types.newobject import newobject - - -_builtin_bytes = bytes - -if PY3: - # We'll probably never use newstr on Py3 anyway... - unicode = str - - -class BaseNewBytes(type): - def __instancecheck__(cls, instance): - if cls == newbytes: - return isinstance(instance, _builtin_bytes) - else: - return issubclass(instance.__class__, cls) - - -def _newchr(x): - if isinstance(x, str): # this happens on pypy - return x.encode('ascii') - else: - return chr(x) - - -class newbytes(with_metaclass(BaseNewBytes, _builtin_bytes)): - """ - A backport of the Python 3 bytes object to Py2 - """ - def __new__(cls, *args, **kwargs): - """ - From the Py3 bytes docstring: - - bytes(iterable_of_ints) -> bytes - bytes(string, encoding[, errors]) -> bytes - bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer - bytes(int) -> bytes object of size given by the parameter initialized with null bytes - bytes() -> empty bytes object - - Construct an immutable array of bytes from: - - an iterable yielding integers in range(256) - - a text string encoded using the specified encoding - - any object implementing the buffer API. - - an integer - """ - - encoding = None - errors = None - - if len(args) == 0: - return super(newbytes, cls).__new__(cls) - elif len(args) >= 2: - args = list(args) - if len(args) == 3: - errors = args.pop() - encoding=args.pop() - # Was: elif isinstance(args[0], newbytes): - # We use type() instead of the above because we're redefining - # this to be True for all unicode string subclasses. Warning: - # This may render newstr un-subclassable. - if type(args[0]) == newbytes: - # Special-case: for consistency with Py3.3, we return the same object - # (with the same id) if a newbytes object is passed into the - # newbytes constructor. - return args[0] - elif isinstance(args[0], _builtin_bytes): - value = args[0] - elif isinstance(args[0], unicode): - try: - if 'encoding' in kwargs: - assert encoding is None - encoding = kwargs['encoding'] - if 'errors' in kwargs: - assert errors is None - errors = kwargs['errors'] - except AssertionError: - raise TypeError('Argument given by name and position') - if encoding is None: - raise TypeError('unicode string argument without an encoding') - ### - # Was: value = args[0].encode(**kwargs) - # Python 2.6 string encode() method doesn't take kwargs: - # Use this instead: - newargs = [encoding] - if errors is not None: - newargs.append(errors) - value = args[0].encode(*newargs) - ### - elif hasattr(args[0], '__bytes__'): - value = args[0].__bytes__() - elif isinstance(args[0], Iterable): - if len(args[0]) == 0: - # This could be an empty list or tuple. Return b'' as on Py3. - value = b'' - else: - # Was: elif len(args[0])>0 and isinstance(args[0][0], Integral): - # # It's a list of integers - # But then we can't index into e.g. frozensets. Try to proceed - # anyway. - try: - value = bytearray([_newchr(x) for x in args[0]]) - except: - raise ValueError('bytes must be in range(0, 256)') - elif isinstance(args[0], Integral): - if args[0] < 0: - raise ValueError('negative count') - value = b'\x00' * args[0] - else: - value = args[0] - if type(value) == newbytes: - # Above we use type(...) rather than isinstance(...) because the - # newbytes metaclass overrides __instancecheck__. - # oldbytes(value) gives the wrong thing on Py2: the same - # result as str(value) on Py3, e.g. "b'abc'". (Issue #193). - # So we handle this case separately: - return copy.copy(value) - else: - return super(newbytes, cls).__new__(cls, value) - - def __repr__(self): - return 'b' + super(newbytes, self).__repr__() - - def __str__(self): - return 'b' + "'{0}'".format(super(newbytes, self).__str__()) - - def __getitem__(self, y): - value = super(newbytes, self).__getitem__(y) - if isinstance(y, Integral): - return ord(value) - else: - return newbytes(value) - - def __getslice__(self, *args): - return self.__getitem__(slice(*args)) - - def __contains__(self, key): - if isinstance(key, int): - newbyteskey = newbytes([key]) - # Don't use isinstance() here because we only want to catch - # newbytes, not Python 2 str: - elif type(key) == newbytes: - newbyteskey = key - else: - newbyteskey = newbytes(key) - return issubset(list(newbyteskey), list(self)) - - @no(unicode) - def __add__(self, other): - return newbytes(super(newbytes, self).__add__(other)) - - @no(unicode) - def __radd__(self, left): - return newbytes(left) + self - - @no(unicode) - def __mul__(self, other): - return newbytes(super(newbytes, self).__mul__(other)) - - @no(unicode) - def __rmul__(self, other): - return newbytes(super(newbytes, self).__rmul__(other)) - - def join(self, iterable_of_bytes): - errmsg = 'sequence item {0}: expected bytes, {1} found' - if isbytes(iterable_of_bytes) or istext(iterable_of_bytes): - raise TypeError(errmsg.format(0, type(iterable_of_bytes))) - for i, item in enumerate(iterable_of_bytes): - if istext(item): - raise TypeError(errmsg.format(i, type(item))) - return newbytes(super(newbytes, self).join(iterable_of_bytes)) - - @classmethod - def fromhex(cls, string): - # Only on Py2: - return cls(string.replace(' ', '').decode('hex')) - - @no(unicode) - def find(self, sub, *args): - return super(newbytes, self).find(sub, *args) - - @no(unicode) - def rfind(self, sub, *args): - return super(newbytes, self).rfind(sub, *args) - - @no(unicode, (1, 2)) - def replace(self, old, new, *args): - return newbytes(super(newbytes, self).replace(old, new, *args)) - - def encode(self, *args): - raise AttributeError("encode method has been disabled in newbytes") - - def decode(self, encoding='utf-8', errors='strict'): - """ - Returns a newstr (i.e. unicode subclass) - - Decode B using the codec registered for encoding. Default encoding - is 'utf-8'. errors may be given to set a different error - handling scheme. Default is 'strict' meaning that encoding errors raise - a UnicodeDecodeError. Other possible values are 'ignore' and 'replace' - as well as any other name registered with codecs.register_error that is - able to handle UnicodeDecodeErrors. - """ - # Py2 str.encode() takes encoding and errors as optional parameter, - # not keyword arguments as in Python 3 str. - - from future.types.newstr import newstr - - if errors == 'surrogateescape': - from future.utils.surrogateescape import register_surrogateescape - register_surrogateescape() - - return newstr(super(newbytes, self).decode(encoding, errors)) - - # This is currently broken: - # # We implement surrogateescape error handling here in addition rather - # # than relying on the custom error handler from - # # future.utils.surrogateescape to be registered globally, even though - # # that is fine in the case of decoding. (But not encoding: see the - # # comments in newstr.encode()``.) - # - # if errors == 'surrogateescape': - # # Decode char by char - # mybytes = [] - # for code in self: - # # Code is an int - # if 0x80 <= code <= 0xFF: - # b = 0xDC00 + code - # elif code <= 0x7F: - # b = _unichr(c).decode(encoding=encoding) - # else: - # # # It may be a bad byte - # # FIXME: What to do in this case? See the Py3 docs / tests. - # # # Try swallowing it. - # # continue - # # print("RAISE!") - # raise NotASurrogateError - # mybytes.append(b) - # return newbytes(mybytes) - # return newbytes(super(newstr, self).decode(encoding, errors)) - - @no(unicode) - def startswith(self, prefix, *args): - return super(newbytes, self).startswith(prefix, *args) - - @no(unicode) - def endswith(self, prefix, *args): - return super(newbytes, self).endswith(prefix, *args) - - @no(unicode) - def split(self, sep=None, maxsplit=-1): - # Py2 str.split() takes maxsplit as an optional parameter, not as a - # keyword argument as in Python 3 bytes. - parts = super(newbytes, self).split(sep, maxsplit) - return [newbytes(part) for part in parts] - - def splitlines(self, keepends=False): - """ - B.splitlines([keepends]) -> list of lines - - Return a list of the lines in B, breaking at line boundaries. - Line breaks are not included in the resulting list unless keepends - is given and true. - """ - # Py2 str.splitlines() takes keepends as an optional parameter, - # not as a keyword argument as in Python 3 bytes. - parts = super(newbytes, self).splitlines(keepends) - return [newbytes(part) for part in parts] - - @no(unicode) - def rsplit(self, sep=None, maxsplit=-1): - # Py2 str.rsplit() takes maxsplit as an optional parameter, not as a - # keyword argument as in Python 3 bytes. - parts = super(newbytes, self).rsplit(sep, maxsplit) - return [newbytes(part) for part in parts] - - @no(unicode) - def partition(self, sep): - parts = super(newbytes, self).partition(sep) - return tuple(newbytes(part) for part in parts) - - @no(unicode) - def rpartition(self, sep): - parts = super(newbytes, self).rpartition(sep) - return tuple(newbytes(part) for part in parts) - - @no(unicode, (1,)) - def rindex(self, sub, *args): - ''' - S.rindex(sub [,start [,end]]) -> int - - Like S.rfind() but raise ValueError when the substring is not found. - ''' - pos = self.rfind(sub, *args) - if pos == -1: - raise ValueError('substring not found') - - @no(unicode) - def index(self, sub, *args): - ''' - Returns index of sub in bytes. - Raises ValueError if byte is not in bytes and TypeError if can't - be converted bytes or its length is not 1. - ''' - if isinstance(sub, int): - if len(args) == 0: - start, end = 0, len(self) - elif len(args) == 1: - start = args[0] - elif len(args) == 2: - start, end = args - else: - raise TypeError('takes at most 3 arguments') - return list(self)[start:end].index(sub) - if not isinstance(sub, bytes): - try: - sub = self.__class__(sub) - except (TypeError, ValueError): - raise TypeError("can't convert sub to bytes") - try: - return super(newbytes, self).index(sub, *args) - except ValueError: - raise ValueError('substring not found') - - def __eq__(self, other): - if isinstance(other, (_builtin_bytes, bytearray)): - return super(newbytes, self).__eq__(other) - else: - return False - - def __ne__(self, other): - if isinstance(other, _builtin_bytes): - return super(newbytes, self).__ne__(other) - else: - return True - - unorderable_err = 'unorderable types: bytes() and {0}' - - def __lt__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__lt__(other) - - def __le__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__le__(other) - - def __gt__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__gt__(other) - - def __ge__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__ge__(other) - - def __native__(self): - # We can't just feed a newbytes object into str(), because - # newbytes.__str__() returns e.g. "b'blah'", consistent with Py3 bytes. - return super(newbytes, self).__str__() - - def __getattribute__(self, name): - """ - A trick to cause the ``hasattr`` builtin-fn to return False for - the 'encode' method on Py2. - """ - if name in ['encode', u'encode']: - raise AttributeError("encode method has been disabled in newbytes") - return super(newbytes, self).__getattribute__(name) - - @no(unicode) - def rstrip(self, bytes_to_strip=None): - """ - Strip trailing bytes contained in the argument. - If the argument is omitted, strip trailing ASCII whitespace. - """ - return newbytes(super(newbytes, self).rstrip(bytes_to_strip)) - - @no(unicode) - def strip(self, bytes_to_strip=None): - """ - Strip leading and trailing bytes contained in the argument. - If the argument is omitted, strip trailing ASCII whitespace. - """ - return newbytes(super(newbytes, self).strip(bytes_to_strip)) - - def lower(self): - """ - b.lower() -> copy of b - - Return a copy of b with all ASCII characters converted to lowercase. - """ - return newbytes(super(newbytes, self).lower()) - - @no(unicode) - def upper(self): - """ - b.upper() -> copy of b - - Return a copy of b with all ASCII characters converted to uppercase. - """ - return newbytes(super(newbytes, self).upper()) - - @classmethod - @no(unicode) - def maketrans(cls, frm, to): - """ - B.maketrans(frm, to) -> translation table - - Return a translation table (a bytes object of length 256) suitable - for use in the bytes or bytearray translate method where each byte - in frm is mapped to the byte at the same position in to. - The bytes objects frm and to must be of the same length. - """ - return newbytes(string.maketrans(frm, to)) - - -__all__ = ['newbytes'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newdict.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newdict.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newdict.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newdict.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -""" -A dict subclass for Python 2 that behaves like Python 3's dict - -Example use: - ->>> from builtins import dict ->>> d1 = dict() # instead of {} for an empty dict ->>> d2 = dict(key1='value1', key2='value2') - -The keys, values and items methods now return iterators on Python 2.x -(with set-like behaviour on Python 2.7). - ->>> for d in (d1, d2): -... assert not isinstance(d.keys(), list) -... assert not isinstance(d.values(), list) -... assert not isinstance(d.items(), list) -""" - -import sys - -from future.utils import with_metaclass -from future.types.newobject import newobject - - -_builtin_dict = dict -ver = sys.version_info[:2] - - -class BaseNewDict(type): - def __instancecheck__(cls, instance): - if cls == newdict: - return isinstance(instance, _builtin_dict) - else: - return issubclass(instance.__class__, cls) - - -class newdict(with_metaclass(BaseNewDict, _builtin_dict)): - """ - A backport of the Python 3 dict object to Py2 - """ - def items(self): - """ - On Python 2.7+: - D.items() -> a set-like object providing a view on D's items - On Python 2.6: - D.items() -> an iterator over D's items - """ - if ver == (2, 7): - return self.viewitems() - elif ver == (2, 6): - return self.iteritems() - elif ver >= (3, 0): - return self.items() - - def keys(self): - """ - On Python 2.7+: - D.keys() -> a set-like object providing a view on D's keys - On Python 2.6: - D.keys() -> an iterator over D's keys - """ - if ver == (2, 7): - return self.viewkeys() - elif ver == (2, 6): - return self.iterkeys() - elif ver >= (3, 0): - return self.keys() - - def values(self): - """ - On Python 2.7+: - D.values() -> a set-like object providing a view on D's values - On Python 2.6: - D.values() -> an iterator over D's values - """ - if ver == (2, 7): - return self.viewvalues() - elif ver == (2, 6): - return self.itervalues() - elif ver >= (3, 0): - return self.values() - - def __new__(cls, *args, **kwargs): - """ - dict() -> new empty dictionary - dict(mapping) -> new dictionary initialized from a mapping object's - (key, value) pairs - dict(iterable) -> new dictionary initialized as if via: - d = {} - for k, v in iterable: - d[k] = v - dict(**kwargs) -> new dictionary initialized with the name=value pairs - in the keyword argument list. For example: dict(one=1, two=2) - """ - - if len(args) == 0: - return super(newdict, cls).__new__(cls) - elif type(args[0]) == newdict: - value = args[0] - else: - value = args[0] - return super(newdict, cls).__new__(cls, value) - - def __native__(self): - """ - Hook for the future.utils.native() function - """ - return dict(self) - - -__all__ = ['newdict'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newint.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newint.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newint.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newint.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,379 +0,0 @@ -""" -Backport of Python 3's int, based on Py2's long. - -They are very similar. The most notable difference is: - -- representation: trailing L in Python 2 removed in Python 3 -""" -from __future__ import division - -import struct -import collections - -from future.types.newbytes import newbytes -from future.types.newobject import newobject -from future.utils import PY3, isint, istext, isbytes, with_metaclass, native - - -if PY3: - long = int - - -class BaseNewInt(type): - def __instancecheck__(cls, instance): - if cls == newint: - # Special case for Py2 short or long int - return isinstance(instance, (int, long)) - else: - return issubclass(instance.__class__, cls) - - -class newint(with_metaclass(BaseNewInt, long)): - """ - A backport of the Python 3 int object to Py2 - """ - def __new__(cls, x=0, base=10): - """ - From the Py3 int docstring: - - | int(x=0) -> integer - | int(x, base=10) -> integer - | - | Convert a number or string to an integer, or return 0 if no - | arguments are given. If x is a number, return x.__int__(). For - | floating point numbers, this truncates towards zero. - | - | If x is not a number or if base is given, then x must be a string, - | bytes, or bytearray instance representing an integer literal in the - | given base. The literal can be preceded by '+' or '-' and be - | surrounded by whitespace. The base defaults to 10. Valid bases are - | 0 and 2-36. Base 0 means to interpret the base from the string as an - | integer literal. - | >>> int('0b100', base=0) - | 4 - - """ - try: - val = x.__int__() - except AttributeError: - val = x - else: - if not isint(val): - raise TypeError('__int__ returned non-int ({0})'.format( - type(val))) - - if base != 10: - # Explicit base - if not (istext(val) or isbytes(val) or isinstance(val, bytearray)): - raise TypeError( - "int() can't convert non-string with explicit base") - try: - return super(newint, cls).__new__(cls, val, base) - except TypeError: - return super(newint, cls).__new__(cls, newbytes(val), base) - # After here, base is 10 - try: - return super(newint, cls).__new__(cls, val) - except TypeError: - # Py2 long doesn't handle bytearray input with an explicit base, so - # handle this here. - # Py3: int(bytearray(b'10'), 2) == 2 - # Py2: int(bytearray(b'10'), 2) == 2 raises TypeError - # Py2: long(bytearray(b'10'), 2) == 2 raises TypeError - try: - return super(newint, cls).__new__(cls, newbytes(val)) - except: - raise TypeError("newint argument must be a string or a number," - "not '{0}'".format(type(val))) - - def __repr__(self): - """ - Without the L suffix - """ - value = super(newint, self).__repr__() - assert value[-1] == 'L' - return value[:-1] - - def __add__(self, other): - value = super(newint, self).__add__(other) - if value is NotImplemented: - return long(self) + other - return newint(value) - - def __radd__(self, other): - value = super(newint, self).__radd__(other) - if value is NotImplemented: - return other + long(self) - return newint(value) - - def __sub__(self, other): - value = super(newint, self).__sub__(other) - if value is NotImplemented: - return long(self) - other - return newint(value) - - def __rsub__(self, other): - value = super(newint, self).__rsub__(other) - if value is NotImplemented: - return other - long(self) - return newint(value) - - def __mul__(self, other): - value = super(newint, self).__mul__(other) - if isint(value): - return newint(value) - elif value is NotImplemented: - return long(self) * other - return value - - def __rmul__(self, other): - value = super(newint, self).__rmul__(other) - if isint(value): - return newint(value) - elif value is NotImplemented: - return other * long(self) - return value - - def __div__(self, other): - # We override this rather than e.g. relying on object.__div__ or - # long.__div__ because we want to wrap the value in a newint() - # call if other is another int - value = long(self) / other - if isinstance(other, (int, long)): - return newint(value) - else: - return value - - def __rdiv__(self, other): - value = other / long(self) - if isinstance(other, (int, long)): - return newint(value) - else: - return value - - def __idiv__(self, other): - # long has no __idiv__ method. Use __itruediv__ and cast back to - # newint: - value = self.__itruediv__(other) - if isinstance(other, (int, long)): - return newint(value) - else: - return value - - def __truediv__(self, other): - value = super(newint, self).__truediv__(other) - if value is NotImplemented: - value = long(self) / other - return value - - def __rtruediv__(self, other): - return super(newint, self).__rtruediv__(other) - - def __itruediv__(self, other): - # long has no __itruediv__ method - mylong = long(self) - mylong /= other - return mylong - - def __floordiv__(self, other): - return newint(super(newint, self).__floordiv__(other)) - - def __rfloordiv__(self, other): - return newint(super(newint, self).__rfloordiv__(other)) - - def __ifloordiv__(self, other): - # long has no __ifloordiv__ method - mylong = long(self) - mylong //= other - return newint(mylong) - - def __mod__(self, other): - value = super(newint, self).__mod__(other) - if value is NotImplemented: - return long(self) % other - return newint(value) - - def __rmod__(self, other): - value = super(newint, self).__rmod__(other) - if value is NotImplemented: - return other % long(self) - return newint(value) - - def __divmod__(self, other): - value = super(newint, self).__divmod__(other) - if value is NotImplemented: - mylong = long(self) - return (mylong // other, mylong % other) - return (newint(value[0]), newint(value[1])) - - def __rdivmod__(self, other): - value = super(newint, self).__rdivmod__(other) - if value is NotImplemented: - mylong = long(self) - return (other // mylong, other % mylong) - return (newint(value[0]), newint(value[1])) - - def __pow__(self, other): - value = super(newint, self).__pow__(other) - if value is NotImplemented: - return long(self) ** other - return newint(value) - - def __rpow__(self, other): - value = super(newint, self).__rpow__(other) - if value is NotImplemented: - return other ** long(self) - return newint(value) - - def __lshift__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for <<: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__lshift__(other)) - - def __rshift__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for >>: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__rshift__(other)) - - def __and__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for &: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__and__(other)) - - def __or__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for |: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__or__(other)) - - def __xor__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for ^: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__xor__(other)) - - def __neg__(self): - return newint(super(newint, self).__neg__()) - - def __pos__(self): - return newint(super(newint, self).__pos__()) - - def __abs__(self): - return newint(super(newint, self).__abs__()) - - def __invert__(self): - return newint(super(newint, self).__invert__()) - - def __int__(self): - return self - - def __nonzero__(self): - return self.__bool__() - - def __bool__(self): - """ - So subclasses can override this, Py3-style - """ - return super(newint, self).__nonzero__() - - def __native__(self): - return long(self) - - def to_bytes(self, length, byteorder='big', signed=False): - """ - Return an array of bytes representing an integer. - - The integer is represented using length bytes. An OverflowError is - raised if the integer is not representable with the given number of - bytes. - - The byteorder argument determines the byte order used to represent the - integer. If byteorder is 'big', the most significant byte is at the - beginning of the byte array. If byteorder is 'little', the most - significant byte is at the end of the byte array. To request the native - byte order of the host system, use `sys.byteorder' as the byte order value. - - The signed keyword-only argument determines whether two's complement is - used to represent the integer. If signed is False and a negative integer - is given, an OverflowError is raised. - """ - if length < 0: - raise ValueError("length argument must be non-negative") - if length == 0 and self == 0: - return newbytes() - if signed and self < 0: - bits = length * 8 - num = (2**bits) + self - if num <= 0: - raise OverflowError("int too smal to convert") - else: - if self < 0: - raise OverflowError("can't convert negative int to unsigned") - num = self - if byteorder not in ('little', 'big'): - raise ValueError("byteorder must be either 'little' or 'big'") - h = b'%x' % num - s = newbytes((b'0'*(len(h) % 2) + h).zfill(length*2).decode('hex')) - if signed: - high_set = s[0] & 0x80 - if self > 0 and high_set: - raise OverflowError("int too big to convert") - if self < 0 and not high_set: - raise OverflowError("int too small to convert") - if len(s) > length: - raise OverflowError("int too big to convert") - return s if byteorder == 'big' else s[::-1] - - @classmethod - def from_bytes(cls, mybytes, byteorder='big', signed=False): - """ - Return the integer represented by the given array of bytes. - - The mybytes argument must either support the buffer protocol or be an - iterable object producing bytes. Bytes and bytearray are examples of - built-in objects that support the buffer protocol. - - The byteorder argument determines the byte order used to represent the - integer. If byteorder is 'big', the most significant byte is at the - beginning of the byte array. If byteorder is 'little', the most - significant byte is at the end of the byte array. To request the native - byte order of the host system, use `sys.byteorder' as the byte order value. - - The signed keyword-only argument indicates whether two's complement is - used to represent the integer. - """ - if byteorder not in ('little', 'big'): - raise ValueError("byteorder must be either 'little' or 'big'") - if isinstance(mybytes, unicode): - raise TypeError("cannot convert unicode objects to bytes") - # mybytes can also be passed as a sequence of integers on Py3. - # Test for this: - elif isinstance(mybytes, collections.Iterable): - mybytes = newbytes(mybytes) - b = mybytes if byteorder == 'big' else mybytes[::-1] - if len(b) == 0: - b = b'\x00' - # The encode() method has been disabled by newbytes, but Py2's - # str has it: - num = int(native(b).encode('hex'), 16) - if signed and (b[0] & 0x80): - num = num - (2 ** (len(b)*8)) - return cls(num) - - -# def _twos_comp(val, bits): -# """compute the 2's compliment of int value val""" -# if( (val&(1<<(bits-1))) != 0 ): -# val = val - (1<>> from builtins import list ->>> l1 = list() # instead of {} for an empty list ->>> l1.append('hello') ->>> l2 = l1.copy() - -""" - -import sys -import copy - -from future.utils import with_metaclass -from future.types.newobject import newobject - - -_builtin_list = list -ver = sys.version_info[:2] - - -class BaseNewList(type): - def __instancecheck__(cls, instance): - if cls == newlist: - return isinstance(instance, _builtin_list) - else: - return issubclass(instance.__class__, cls) - - -class newlist(with_metaclass(BaseNewList, _builtin_list)): - """ - A backport of the Python 3 list object to Py2 - """ - def copy(self): - """ - L.copy() -> list -- a shallow copy of L - """ - return copy.copy(self) - - def clear(self): - """L.clear() -> None -- remove all items from L""" - for i in range(len(self)): - self.pop() - - def __new__(cls, *args, **kwargs): - """ - list() -> new empty list - list(iterable) -> new list initialized from iterable's items - """ - - if len(args) == 0: - return super(newlist, cls).__new__(cls) - elif type(args[0]) == newlist: - value = args[0] - else: - value = args[0] - return super(newlist, cls).__new__(cls, value) - - def __add__(self, value): - return newlist(super(newlist, self).__add__(value)) - - def __radd__(self, left): - " left + self " - try: - return newlist(left) + self - except: - return NotImplemented - - def __getitem__(self, y): - """ - x.__getitem__(y) <==> x[y] - - Warning: a bug in Python 2.x prevents indexing via a slice from - returning a newlist object. - """ - if isinstance(y, slice): - return newlist(super(newlist, self).__getitem__(y)) - else: - return super(newlist, self).__getitem__(y) - - def __native__(self): - """ - Hook for the future.utils.native() function - """ - return list(self) - - def __nonzero__(self): - return len(self) > 0 - - -__all__ = ['newlist'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newmemoryview.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newmemoryview.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newmemoryview.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newmemoryview.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -""" -A pretty lame implementation of a memoryview object for Python 2.6. -""" - -from collections import Iterable -from numbers import Integral -import string - -from future.utils import istext, isbytes, PY3, with_metaclass -from future.types import no, issubset - - -# class BaseNewBytes(type): -# def __instancecheck__(cls, instance): -# return isinstance(instance, _builtin_bytes) - - -class newmemoryview(object): # with_metaclass(BaseNewBytes, _builtin_bytes)): - """ - A pretty lame backport of the Python 2.7 and Python 3.x - memoryviewview object to Py2.6. - """ - def __init__(self, obj): - return obj - - -__all__ = ['newmemoryview'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newobject.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newobject.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newobject.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newobject.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -""" -An object subclass for Python 2 that gives new-style classes written in the -style of Python 3 (with ``__next__`` and unicode-returning ``__str__`` methods) -the appropriate Python 2-style ``next`` and ``__unicode__`` methods for compatible. - -Example use:: - - from builtins import object - - my_unicode_str = u'Unicode string: \u5b54\u5b50' - - class A(object): - def __str__(self): - return my_unicode_str - - a = A() - print(str(a)) - - # On Python 2, these relations hold: - assert unicode(a) == my_unicode_string - assert str(a) == my_unicode_string.encode('utf-8') - - -Another example:: - - from builtins import object - - class Upper(object): - def __init__(self, iterable): - self._iter = iter(iterable) - def __next__(self): # note the Py3 interface - return next(self._iter).upper() - def __iter__(self): - return self - - assert list(Upper('hello')) == list('HELLO') - -""" - -import sys - -from future.utils import with_metaclass - - -_builtin_object = object -ver = sys.version_info[:2] - - -# We no longer define a metaclass for newobject because this breaks multiple -# inheritance and custom metaclass use with this exception: - -# TypeError: Error when calling the metaclass bases -# metaclass conflict: the metaclass of a derived class must be a -# (non-strict) subclass of the metaclasses of all its bases - -# See issues #91 and #96. - - -class newobject(object): - """ - A magical object class that provides Python 2 compatibility methods:: - next - __unicode__ - __nonzero__ - - Subclasses of this class can merely define the Python 3 methods (__next__, - __str__, and __bool__). - """ - def next(self): - if hasattr(self, '__next__'): - return type(self).__next__(self) - raise TypeError('newobject is not an iterator') - - def __unicode__(self): - # All subclasses of the builtin object should have __str__ defined. - # Note that old-style classes do not have __str__ defined. - if hasattr(self, '__str__'): - s = type(self).__str__(self) - else: - s = str(self) - if isinstance(s, unicode): - return s - else: - return s.decode('utf-8') - - def __nonzero__(self): - if hasattr(self, '__bool__'): - return type(self).__bool__(self) - if hasattr(self, '__len__'): - return type(self).__len__(self) - # object has no __nonzero__ method - return True - - # Are these ever needed? - # def __div__(self): - # return self.__truediv__() - - # def __idiv__(self, other): - # return self.__itruediv__(other) - - def __long__(self): - if not hasattr(self, '__int__'): - return NotImplemented - return self.__int__() # not type(self).__int__(self) - - # def __new__(cls, *args, **kwargs): - # """ - # dict() -> new empty dictionary - # dict(mapping) -> new dictionary initialized from a mapping object's - # (key, value) pairs - # dict(iterable) -> new dictionary initialized as if via: - # d = {} - # for k, v in iterable: - # d[k] = v - # dict(**kwargs) -> new dictionary initialized with the name=value pairs - # in the keyword argument list. For example: dict(one=1, two=2) - # """ - - # if len(args) == 0: - # return super(newdict, cls).__new__(cls) - # elif type(args[0]) == newdict: - # return args[0] - # else: - # value = args[0] - # return super(newdict, cls).__new__(cls, value) - - def __native__(self): - """ - Hook for the future.utils.native() function - """ - return object(self) - - -__all__ = ['newobject'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newopen.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newopen.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newopen.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newopen.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -""" -A substitute for the Python 3 open() function. - -Note that io.open() is more complete but maybe slower. Even so, the -completeness may be a better default. TODO: compare these -""" - -_builtin_open = open - -class newopen(object): - """Wrapper providing key part of Python 3 open() interface. - - From IPython's py3compat.py module. License: BSD. - """ - def __init__(self, fname, mode="r", encoding="utf-8"): - self.f = _builtin_open(fname, mode) - self.enc = encoding - - def write(self, s): - return self.f.write(s.encode(self.enc)) - - def read(self, size=-1): - return self.f.read(size).decode(self.enc) - - def close(self): - return self.f.close() - - def __enter__(self): - return self - - def __exit__(self, etype, value, traceback): - self.f.close() - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newrange.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newrange.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newrange.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newrange.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -""" -Nearly identical to xrange.py, by Dan Crosta, from - - https://github.com/dcrosta/xrange.git - -This is included here in the ``future`` package rather than pointed to as -a dependency because there is no package for ``xrange`` on PyPI. It is -also tweaked to appear like a regular Python 3 ``range`` object rather -than a Python 2 xrange. - -From Dan Crosta's README: - - "A pure-Python implementation of Python 2.7's xrange built-in, with - some features backported from the Python 3.x range built-in (which - replaced xrange) in that version." - - Read more at - https://late.am/post/2012/06/18/what-the-heck-is-an-xrange -""" -from __future__ import absolute_import - -from collections import Sequence, Iterator -from itertools import islice - -from future.backports.misc import count # with step parameter on Py2.6 -# For backward compatibility with python-future versions < 0.14.4: -_count = count - - -class newrange(Sequence): - """ - Pure-Python backport of Python 3's range object. See `the CPython - documentation for details: - `_ - """ - - def __init__(self, *args): - if len(args) == 1: - start, stop, step = 0, args[0], 1 - elif len(args) == 2: - start, stop, step = args[0], args[1], 1 - elif len(args) == 3: - start, stop, step = args - else: - raise TypeError('range() requires 1-3 int arguments') - - try: - start, stop, step = int(start), int(stop), int(step) - except ValueError: - raise TypeError('an integer is required') - - if step == 0: - raise ValueError('range() arg 3 must not be zero') - elif step < 0: - stop = min(stop, start) - else: - stop = max(stop, start) - - self._start = start - self._stop = stop - self._step = step - self._len = (stop - start) // step + bool((stop - start) % step) - - @property - def start(self): - return self._start - - @property - def stop(self): - return self._stop - - @property - def step(self): - return self._step - - def __repr__(self): - if self._step == 1: - return 'range(%d, %d)' % (self._start, self._stop) - return 'range(%d, %d, %d)' % (self._start, self._stop, self._step) - - def __eq__(self, other): - return (isinstance(other, newrange) and - (self._len == 0 == other._len or - (self._start, self._step, self._len) == - (other._start, other._step, self._len))) - - def __len__(self): - return self._len - - def index(self, value): - """Return the 0-based position of integer `value` in - the sequence this range represents.""" - diff = value - self._start - quotient, remainder = divmod(diff, self._step) - if remainder == 0 and 0 <= quotient < self._len: - return abs(quotient) - raise ValueError('%r is not in range' % value) - - def count(self, value): - """Return the number of ocurrences of integer `value` - in the sequence this range represents.""" - # a value can occur exactly zero or one times - return int(value in self) - - def __contains__(self, value): - """Return ``True`` if the integer `value` occurs in - the sequence this range represents.""" - try: - self.index(value) - return True - except ValueError: - return False - - def __reversed__(self): - return iter(self[::-1]) - - def __getitem__(self, index): - """Return the element at position ``index`` in the sequence - this range represents, or raise :class:`IndexError` if the - position is out of range.""" - if isinstance(index, slice): - return self.__getitem_slice(index) - if index < 0: - # negative indexes access from the end - index = self._len + index - if index < 0 or index >= self._len: - raise IndexError('range object index out of range') - return self._start + index * self._step - - def __getitem_slice(self, slce): - """Return a range which represents the requested slce - of the sequence represented by this range. - """ - scaled_indices = (self._step * n for n in slce.indices(self._len)) - start_offset, stop_offset, new_step = scaled_indices - return newrange(self._start + start_offset, - self._start + stop_offset, - new_step) - - def __iter__(self): - """Return an iterator which enumerates the elements of the - sequence this range represents.""" - return range_iterator(self) - - -class range_iterator(Iterator): - """An iterator for a :class:`range`. - """ - def __init__(self, range_): - self._stepper = islice(count(range_.start, range_.step), len(range_)) - - def __iter__(self): - return self - - def next(self): - return next(self._stepper) - - -__all__ = ['newrange'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newstr.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newstr.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/types/newstr.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/types/newstr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,412 +0,0 @@ -""" -This module redefines ``str`` on Python 2.x to be a subclass of the Py2 -``unicode`` type that behaves like the Python 3.x ``str``. - -The main differences between ``newstr`` and Python 2.x's ``unicode`` type are -the stricter type-checking and absence of a `u''` prefix in the representation. - -It is designed to be used together with the ``unicode_literals`` import -as follows: - - >>> from __future__ import unicode_literals - >>> from builtins import str, isinstance - -On Python 3.x and normally on Python 2.x, these expressions hold - - >>> str('blah') is 'blah' - True - >>> isinstance('blah', str) - True - -However, on Python 2.x, with this import: - - >>> from __future__ import unicode_literals - -the same expressions are False: - - >>> str('blah') is 'blah' - False - >>> isinstance('blah', str) - False - -This module is designed to be imported together with ``unicode_literals`` on -Python 2 to bring the meaning of ``str`` back into alignment with unprefixed -string literals (i.e. ``unicode`` subclasses). - -Note that ``str()`` (and ``print()``) would then normally call the -``__unicode__`` method on objects in Python 2. To define string -representations of your objects portably across Py3 and Py2, use the -:func:`python_2_unicode_compatible` decorator in :mod:`future.utils`. - -""" - -from collections import Iterable -from numbers import Number - -from future.utils import PY3, istext, with_metaclass, isnewbytes -from future.types import no, issubset -from future.types.newobject import newobject - - -if PY3: - # We'll probably never use newstr on Py3 anyway... - unicode = str - - -class BaseNewStr(type): - def __instancecheck__(cls, instance): - if cls == newstr: - return isinstance(instance, unicode) - else: - return issubclass(instance.__class__, cls) - - -class newstr(with_metaclass(BaseNewStr, unicode)): - """ - A backport of the Python 3 str object to Py2 - """ - no_convert_msg = "Can't convert '{0}' object to str implicitly" - - def __new__(cls, *args, **kwargs): - """ - From the Py3 str docstring: - - str(object='') -> str - str(bytes_or_buffer[, encoding[, errors]]) -> str - - Create a new string object from the given object. If encoding or - errors is specified, then the object must expose a data buffer - that will be decoded using the given encoding and error handler. - Otherwise, returns the result of object.__str__() (if defined) - or repr(object). - encoding defaults to sys.getdefaultencoding(). - errors defaults to 'strict'. - - """ - if len(args) == 0: - return super(newstr, cls).__new__(cls) - # Special case: If someone requests str(str(u'abc')), return the same - # object (same id) for consistency with Py3.3. This is not true for - # other objects like list or dict. - elif type(args[0]) == newstr and cls == newstr: - return args[0] - elif isinstance(args[0], unicode): - value = args[0] - elif isinstance(args[0], bytes): # i.e. Py2 bytes or newbytes - if 'encoding' in kwargs or len(args) > 1: - value = args[0].decode(*args[1:], **kwargs) - else: - value = args[0].__str__() - else: - value = args[0] - return super(newstr, cls).__new__(cls, value) - - def __repr__(self): - """ - Without the u prefix - """ - value = super(newstr, self).__repr__() - # assert value[0] == u'u' - return value[1:] - - def __getitem__(self, y): - """ - Warning: Python <= 2.7.6 has a bug that causes this method never to be called - when y is a slice object. Therefore the type of newstr()[:2] is wrong - (unicode instead of newstr). - """ - return newstr(super(newstr, self).__getitem__(y)) - - def __contains__(self, key): - errmsg = "'in ' requires string as left operand, not {0}" - # Don't use isinstance() here because we only want to catch - # newstr, not Python 2 unicode: - if type(key) == newstr: - newkey = key - elif isinstance(key, unicode) or isinstance(key, bytes) and not isnewbytes(key): - newkey = newstr(key) - else: - raise TypeError(errmsg.format(type(key))) - return issubset(list(newkey), list(self)) - - @no('newbytes') - def __add__(self, other): - return newstr(super(newstr, self).__add__(other)) - - @no('newbytes') - def __radd__(self, left): - " left + self " - try: - return newstr(left) + self - except: - return NotImplemented - - def __mul__(self, other): - return newstr(super(newstr, self).__mul__(other)) - - def __rmul__(self, other): - return newstr(super(newstr, self).__rmul__(other)) - - def join(self, iterable): - errmsg = 'sequence item {0}: expected unicode string, found bytes' - for i, item in enumerate(iterable): - # Here we use type() rather than isinstance() because - # __instancecheck__ is being overridden. E.g. - # isinstance(b'abc', newbytes) is True on Py2. - if isnewbytes(item): - raise TypeError(errmsg.format(i)) - # Support use as a staticmethod: str.join('-', ['a', 'b']) - if type(self) == newstr: - return newstr(super(newstr, self).join(iterable)) - else: - return newstr(super(newstr, newstr(self)).join(iterable)) - - @no('newbytes') - def find(self, sub, *args): - return super(newstr, self).find(sub, *args) - - @no('newbytes') - def rfind(self, sub, *args): - return super(newstr, self).rfind(sub, *args) - - @no('newbytes', (1, 2)) - def replace(self, old, new, *args): - return newstr(super(newstr, self).replace(old, new, *args)) - - def decode(self, *args): - raise AttributeError("decode method has been disabled in newstr") - - def encode(self, encoding='utf-8', errors='strict'): - """ - Returns bytes - - Encode S using the codec registered for encoding. Default encoding - is 'utf-8'. errors may be given to set a different error - handling scheme. Default is 'strict' meaning that encoding errors raise - a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and - 'xmlcharrefreplace' as well as any other name registered with - codecs.register_error that can handle UnicodeEncodeErrors. - """ - from future.types.newbytes import newbytes - # Py2 unicode.encode() takes encoding and errors as optional parameter, - # not keyword arguments as in Python 3 str. - - # For the surrogateescape error handling mechanism, the - # codecs.register_error() function seems to be inadequate for an - # implementation of it when encoding. (Decoding seems fine, however.) - # For example, in the case of - # u'\udcc3'.encode('ascii', 'surrogateescape_handler') - # after registering the ``surrogateescape_handler`` function in - # future.utils.surrogateescape, both Python 2.x and 3.x raise an - # exception anyway after the function is called because the unicode - # string it has to return isn't encodable strictly as ASCII. - - if errors == 'surrogateescape': - if encoding == 'utf-16': - # Known to fail here. See test_encoding_works_normally() - raise NotImplementedError('FIXME: surrogateescape handling is ' - 'not yet implemented properly') - # Encode char by char, building up list of byte-strings - mybytes = [] - for c in self: - code = ord(c) - if 0xD800 <= code <= 0xDCFF: - mybytes.append(newbytes([code - 0xDC00])) - else: - mybytes.append(c.encode(encoding=encoding)) - return newbytes(b'').join(mybytes) - return newbytes(super(newstr, self).encode(encoding, errors)) - - @no('newbytes', 1) - def startswith(self, prefix, *args): - if isinstance(prefix, Iterable): - for thing in prefix: - if isnewbytes(thing): - raise TypeError(self.no_convert_msg.format(type(thing))) - return super(newstr, self).startswith(prefix, *args) - - @no('newbytes', 1) - def endswith(self, prefix, *args): - # Note we need the decorator above as well as the isnewbytes() - # check because prefix can be either a bytes object or e.g. a - # tuple of possible prefixes. (If it's a bytes object, each item - # in it is an int.) - if isinstance(prefix, Iterable): - for thing in prefix: - if isnewbytes(thing): - raise TypeError(self.no_convert_msg.format(type(thing))) - return super(newstr, self).endswith(prefix, *args) - - @no('newbytes', 1) - def split(self, sep=None, maxsplit=-1): - # Py2 unicode.split() takes maxsplit as an optional parameter, - # not as a keyword argument as in Python 3 str. - parts = super(newstr, self).split(sep, maxsplit) - return [newstr(part) for part in parts] - - @no('newbytes', 1) - def rsplit(self, sep=None, maxsplit=-1): - # Py2 unicode.rsplit() takes maxsplit as an optional parameter, - # not as a keyword argument as in Python 3 str. - parts = super(newstr, self).rsplit(sep, maxsplit) - return [newstr(part) for part in parts] - - @no('newbytes', 1) - def partition(self, sep): - parts = super(newstr, self).partition(sep) - return tuple(newstr(part) for part in parts) - - @no('newbytes', 1) - def rpartition(self, sep): - parts = super(newstr, self).rpartition(sep) - return tuple(newstr(part) for part in parts) - - @no('newbytes', 1) - def index(self, sub, *args): - """ - Like newstr.find() but raise ValueError when the substring is not - found. - """ - pos = self.find(sub, *args) - if pos == -1: - raise ValueError('substring not found') - return pos - - def splitlines(self, keepends=False): - """ - S.splitlines(keepends=False) -> list of strings - - Return a list of the lines in S, breaking at line boundaries. - Line breaks are not included in the resulting list unless keepends - is given and true. - """ - # Py2 unicode.splitlines() takes keepends as an optional parameter, - # not as a keyword argument as in Python 3 str. - parts = super(newstr, self).splitlines(keepends) - return [newstr(part) for part in parts] - - def __eq__(self, other): - if (isinstance(other, unicode) or - isinstance(other, bytes) and not isnewbytes(other)): - return super(newstr, self).__eq__(other) - else: - return False - - def __ne__(self, other): - if (isinstance(other, unicode) or - isinstance(other, bytes) and not isnewbytes(other)): - return super(newstr, self).__ne__(other) - else: - return True - - unorderable_err = 'unorderable types: str() and {0}' - - def __lt__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__lt__(other) - - def __le__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__le__(other) - - def __gt__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__gt__(other) - - def __ge__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__ge__(other) - - def __getattribute__(self, name): - """ - A trick to cause the ``hasattr`` builtin-fn to return False for - the 'decode' method on Py2. - """ - if name in ['decode', u'decode']: - raise AttributeError("decode method has been disabled in newstr") - return super(newstr, self).__getattribute__(name) - - def __native__(self): - """ - A hook for the future.utils.native() function. - """ - return unicode(self) - - @staticmethod - def maketrans(x, y=None, z=None): - """ - Return a translation table usable for str.translate(). - - If there is only one argument, it must be a dictionary mapping Unicode - ordinals (integers) or characters to Unicode ordinals, strings or None. - Character keys will be then converted to ordinals. - If there are two arguments, they must be strings of equal length, and - in the resulting dictionary, each character in x will be mapped to the - character at the same position in y. If there is a third argument, it - must be a string, whose characters will be mapped to None in the result. - """ - - if y is None: - assert z is None - if not isinstance(x, dict): - raise TypeError('if you give only one argument to maketrans it must be a dict') - result = {} - for (key, value) in x.items(): - if len(key) > 1: - raise ValueError('keys in translate table must be strings or integers') - result[ord(key)] = value - else: - if not isinstance(x, unicode) and isinstance(y, unicode): - raise TypeError('x and y must be unicode strings') - if not len(x) == len(y): - raise ValueError('the first two maketrans arguments must have equal length') - result = {} - for (xi, yi) in zip(x, y): - if len(xi) > 1: - raise ValueError('keys in translate table must be strings or integers') - result[ord(xi)] = ord(yi) - - if z is not None: - for char in z: - result[ord(char)] = None - return result - - def translate(self, table): - """ - S.translate(table) -> str - - Return a copy of the string S, where all characters have been mapped - through the given translation table, which must be a mapping of - Unicode ordinals to Unicode ordinals, strings, or None. - Unmapped characters are left untouched. Characters mapped to None - are deleted. - """ - l = [] - for c in self: - if ord(c) in table: - val = table[ord(c)] - if val is None: - continue - elif isinstance(val, unicode): - l.append(val) - else: - l.append(chr(val)) - else: - l.append(c) - return ''.join(l) - - def isprintable(self): - raise NotImplementedError('fixme') - - def isidentifier(self): - raise NotImplementedError('fixme') - - def format_map(self): - raise NotImplementedError('fixme') - - -__all__ = ['newstr'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/utils/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/utils/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/utils/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/utils/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,739 +0,0 @@ -""" -A selection of cross-compatible functions for Python 2 and 3. - -This module exports useful functions for 2/3 compatible code: - - * bind_method: binds functions to classes - * ``native_str_to_bytes`` and ``bytes_to_native_str`` - * ``native_str``: always equal to the native platform string object (because - this may be shadowed by imports from future.builtins) - * lists: lrange(), lmap(), lzip(), lfilter() - * iterable method compatibility: - - iteritems, iterkeys, itervalues - - viewitems, viewkeys, viewvalues - - These use the original method if available, otherwise they use items, - keys, values. - - * types: - - * text_type: unicode in Python 2, str in Python 3 - * binary_type: str in Python 2, bythes in Python 3 - * string_types: basestring in Python 2, str in Python 3 - - * bchr(c): - Take an integer and make a 1-character byte string - * bord(c) - Take the result of indexing on a byte string and make an integer - * tobytes(s) - Take a text string, a byte string, or a sequence of characters taken - from a byte string, and make a byte string. - - * raise_from() - * raise_with_traceback() - -This module also defines these decorators: - - * ``python_2_unicode_compatible`` - * ``with_metaclass`` - * ``implements_iterator`` - -Some of the functions in this module come from the following sources: - - * Jinja2 (BSD licensed: see - https://github.com/mitsuhiko/jinja2/blob/master/LICENSE) - * Pandas compatibility module pandas.compat - * six.py by Benjamin Peterson - * Django -""" - -import types -import sys -import numbers -import functools -import copy -import inspect - - -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 -PY26 = sys.version_info[0:2] == (2, 6) -PY27 = sys.version_info[0:2] == (2, 7) -PYPY = hasattr(sys, 'pypy_translation_info') - - -def python_2_unicode_compatible(cls): - """ - A decorator that defines __unicode__ and __str__ methods under Python - 2. Under Python 3, this decorator is a no-op. - - To support Python 2 and 3 with a single code base, define a __str__ - method returning unicode text and apply this decorator to the class, like - this:: - - >>> from future.utils import python_2_unicode_compatible - - >>> @python_2_unicode_compatible - ... class MyClass(object): - ... def __str__(self): - ... return u'Unicode string: \u5b54\u5b50' - - >>> a = MyClass() - - Then, after this import: - - >>> from future.builtins import str - - the following is ``True`` on both Python 3 and 2:: - - >>> str(a) == a.encode('utf-8').decode('utf-8') - True - - and, on a Unicode-enabled terminal with the right fonts, these both print the - Chinese characters for Confucius:: - - >>> print(a) - >>> print(str(a)) - - The implementation comes from django.utils.encoding. - """ - if not PY3: - cls.__unicode__ = cls.__str__ - cls.__str__ = lambda self: self.__unicode__().encode('utf-8') - return cls - - -def with_metaclass(meta, *bases): - """ - Function from jinja2/_compat.py. License: BSD. - - Use it like this:: - - class BaseForm(object): - pass - - class FormType(type): - pass - - class Form(with_metaclass(FormType, BaseForm)): - pass - - 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. Because of internal type checks - we also need to make sure that we downgrade the custom metaclass - for one level to something closer to type (that's why __call__ and - __init__ comes back from type etc.). - - This has the advantage over six.with_metaclass of not introducing - dummy classes into the final MRO. - """ - class metaclass(meta): - __call__ = type.__call__ - __init__ = type.__init__ - def __new__(cls, name, this_bases, d): - if this_bases is None: - return type.__new__(cls, name, (), d) - return meta(name, bases, d) - return metaclass('temporary_class', None, {}) - - -# Definitions from pandas.compat and six.py follow: -if PY3: - def bchr(s): - return bytes([s]) - def bstr(s): - if isinstance(s, str): - return bytes(s, 'latin-1') - else: - return bytes(s) - def bord(s): - return s - - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - -else: - # Python 2 - def bchr(s): - return chr(s) - def bstr(s): - return str(s) - def bord(s): - return ord(s) - - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - -### - -if PY3: - def tobytes(s): - if isinstance(s, bytes): - return s - else: - if isinstance(s, str): - return s.encode('latin-1') - else: - return bytes(s) -else: - # Python 2 - def tobytes(s): - if isinstance(s, unicode): - return s.encode('latin-1') - else: - return ''.join(s) - -tobytes.__doc__ = """ - Encodes to latin-1 (where the first 256 chars are the same as - ASCII.) - """ - -if PY3: - def native_str_to_bytes(s, encoding='utf-8'): - return s.encode(encoding) - - def bytes_to_native_str(b, encoding='utf-8'): - return b.decode(encoding) - - def text_to_native_str(t, encoding=None): - return t -else: - # Python 2 - def native_str_to_bytes(s, encoding=None): - from future.types import newbytes # to avoid a circular import - return newbytes(s) - - def bytes_to_native_str(b, encoding=None): - return native(b) - - def text_to_native_str(t, encoding='ascii'): - """ - Use this to create a Py2 native string when "from __future__ import - unicode_literals" is in effect. - """ - return unicode(t).encode(encoding) - -native_str_to_bytes.__doc__ = """ - On Py3, returns an encoded string. - On Py2, returns a newbytes type, ignoring the ``encoding`` argument. - """ - -if PY3: - # list-producing versions of the major Python iterating functions - def lrange(*args, **kwargs): - return list(range(*args, **kwargs)) - - def lzip(*args, **kwargs): - return list(zip(*args, **kwargs)) - - def lmap(*args, **kwargs): - return list(map(*args, **kwargs)) - - def lfilter(*args, **kwargs): - return list(filter(*args, **kwargs)) -else: - import __builtin__ - # Python 2-builtin ranges produce lists - lrange = __builtin__.range - lzip = __builtin__.zip - lmap = __builtin__.map - lfilter = __builtin__.filter - - -def isidentifier(s, dotted=False): - ''' - A function equivalent to the str.isidentifier method on Py3 - ''' - if dotted: - return all(isidentifier(a) for a in s.split('.')) - if PY3: - return s.isidentifier() - else: - import re - _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") - return bool(_name_re.match(s)) - - -def viewitems(obj, **kwargs): - """ - Function for iterating over dictionary items with the same set-like - behaviour on Py2.7 as on Py3. - - Passes kwargs to method.""" - func = getattr(obj, "viewitems", None) - if not func: - func = obj.items - return func(**kwargs) - - -def viewkeys(obj, **kwargs): - """ - Function for iterating over dictionary keys with the same set-like - behaviour on Py2.7 as on Py3. - - Passes kwargs to method.""" - func = getattr(obj, "viewkeys", None) - if not func: - func = obj.keys - return func(**kwargs) - - -def viewvalues(obj, **kwargs): - """ - Function for iterating over dictionary values with the same set-like - behaviour on Py2.7 as on Py3. - - Passes kwargs to method.""" - func = getattr(obj, "viewvalues", None) - if not func: - func = obj.values - return func(**kwargs) - - -def iteritems(obj, **kwargs): - """Use this only if compatibility with Python versions before 2.7 is - required. Otherwise, prefer viewitems(). - """ - func = getattr(obj, "iteritems", None) - if not func: - func = obj.items - return func(**kwargs) - - -def iterkeys(obj, **kwargs): - """Use this only if compatibility with Python versions before 2.7 is - required. Otherwise, prefer viewkeys(). - """ - func = getattr(obj, "iterkeys", None) - if not func: - func = obj.keys - return func(**kwargs) - - -def itervalues(obj, **kwargs): - """Use this only if compatibility with Python versions before 2.7 is - required. Otherwise, prefer viewvalues(). - """ - func = getattr(obj, "itervalues", None) - if not func: - func = obj.values - return func(**kwargs) - - -def bind_method(cls, name, func): - """Bind a method to class, python 2 and python 3 compatible. - - Parameters - ---------- - - cls : type - class to receive bound method - name : basestring - name of method on class instance - func : function - function to be bound as method - - Returns - ------- - None - """ - # only python 2 has an issue with bound/unbound methods - if not PY3: - setattr(cls, name, types.MethodType(func, None, cls)) - else: - setattr(cls, name, func) - - -def getexception(): - return sys.exc_info()[1] - - -def _get_caller_globals_and_locals(): - """ - Returns the globals and locals of the calling frame. - - Is there an alternative to frame hacking here? - """ - caller_frame = inspect.stack()[2] - myglobals = caller_frame[0].f_globals - mylocals = caller_frame[0].f_locals - return myglobals, mylocals - - -def _repr_strip(mystring): - """ - Returns the string without any initial or final quotes. - """ - r = repr(mystring) - if r.startswith("'") and r.endswith("'"): - return r[1:-1] - else: - return r - - -if PY3: - def raise_from(exc, cause): - """ - Equivalent to: - - raise EXCEPTION from CAUSE - - on Python 3. (See PEP 3134). - """ - myglobals, mylocals = _get_caller_globals_and_locals() - - # We pass the exception and cause along with other globals - # when we exec(): - myglobals = myglobals.copy() - myglobals['__python_future_raise_from_exc'] = exc - myglobals['__python_future_raise_from_cause'] = cause - execstr = "raise __python_future_raise_from_exc from __python_future_raise_from_cause" - exec(execstr, myglobals, mylocals) - - def raise_(tp, value=None, tb=None): - """ - A function that matches the Python 2.x ``raise`` statement. This - allows re-raising exceptions with the cls value and traceback on - Python 2 and 3. - """ - if value is not None and isinstance(tp, Exception): - raise TypeError("instance exception may not have a separate value") - if value is not None: - exc = tp(value) - else: - exc = tp - if exc.__traceback__ is not tb: - raise exc.with_traceback(tb) - raise exc - - def raise_with_traceback(exc, traceback=Ellipsis): - if traceback == Ellipsis: - _, _, traceback = sys.exc_info() - raise exc.with_traceback(traceback) - -else: - def raise_from(exc, cause): - """ - Equivalent to: - - raise EXCEPTION from CAUSE - - on Python 3. (See PEP 3134). - """ - # Is either arg an exception class (e.g. IndexError) rather than - # instance (e.g. IndexError('my message here')? If so, pass the - # name of the class undisturbed through to "raise ... from ...". - if isinstance(exc, type) and issubclass(exc, Exception): - e = exc() - # exc = exc.__name__ - # execstr = "e = " + _repr_strip(exc) + "()" - # myglobals, mylocals = _get_caller_globals_and_locals() - # exec(execstr, myglobals, mylocals) - else: - e = exc - e.__suppress_context__ = False - if isinstance(cause, type) and issubclass(cause, Exception): - e.__cause__ = cause() - e.__suppress_context__ = True - elif cause is None: - e.__cause__ = None - e.__suppress_context__ = True - elif isinstance(cause, BaseException): - e.__cause__ = cause - e.__suppress_context__ = True - else: - raise TypeError("exception causes must derive from BaseException") - e.__context__ = sys.exc_info()[1] - raise e - - exec(''' -def raise_(tp, value=None, tb=None): - raise tp, value, tb - -def raise_with_traceback(exc, traceback=Ellipsis): - if traceback == Ellipsis: - _, _, traceback = sys.exc_info() - raise exc, None, traceback -'''.strip()) - - -raise_with_traceback.__doc__ = ( -"""Raise exception with existing traceback. -If traceback is not passed, uses sys.exc_info() to get traceback.""" -) - - -# Deprecated alias for backward compatibility with ``future`` versions < 0.11: -reraise = raise_ - - -def implements_iterator(cls): - ''' - From jinja2/_compat.py. License: BSD. - - Use as a decorator like this:: - - @implements_iterator - class UppercasingIterator(object): - def __init__(self, iterable): - self._iter = iter(iterable) - def __iter__(self): - return self - def __next__(self): - return next(self._iter).upper() - - ''' - if PY3: - return cls - else: - cls.next = cls.__next__ - del cls.__next__ - return cls - -if PY3: - get_next = lambda x: x.next -else: - get_next = lambda x: x.__next__ - - -def encode_filename(filename): - if PY3: - return filename - else: - if isinstance(filename, unicode): - return filename.encode('utf-8') - return filename - - -def is_new_style(cls): - """ - Python 2.7 has both new-style and old-style classes. Old-style classes can - be pesky in some circumstances, such as when using inheritance. Use this - function to test for whether a class is new-style. (Python 3 only has - new-style classes.) - """ - return hasattr(cls, '__class__') and ('__dict__' in dir(cls) - or hasattr(cls, '__slots__')) - -# The native platform string and bytes types. Useful because ``str`` and -# ``bytes`` are redefined on Py2 by ``from future.builtins import *``. -native_str = str -native_bytes = bytes - - -def istext(obj): - """ - Deprecated. Use:: - >>> isinstance(obj, str) - after this import: - >>> from future.builtins import str - """ - return isinstance(obj, type(u'')) - - -def isbytes(obj): - """ - Deprecated. Use:: - >>> isinstance(obj, bytes) - after this import: - >>> from future.builtins import bytes - """ - return isinstance(obj, type(b'')) - - -def isnewbytes(obj): - """ - Equivalent to the result of ``isinstance(obj, newbytes)`` were - ``__instancecheck__`` not overridden on the newbytes subclass. In - other words, it is REALLY a newbytes instance, not a Py2 native str - object? - """ - # TODO: generalize this so that it works with subclasses of newbytes - # Import is here to avoid circular imports: - from future.types.newbytes import newbytes - return type(obj) == newbytes - - -def isint(obj): - """ - Deprecated. Tests whether an object is a Py3 ``int`` or either a Py2 ``int`` or - ``long``. - - Instead of using this function, you can use: - - >>> from future.builtins import int - >>> isinstance(obj, int) - - The following idiom is equivalent: - - >>> from numbers import Integral - >>> isinstance(obj, Integral) - """ - - return isinstance(obj, numbers.Integral) - - -def native(obj): - """ - On Py3, this is a no-op: native(obj) -> obj - - On Py2, returns the corresponding native Py2 types that are - superclasses for backported objects from Py3: - - >>> from builtins import str, bytes, int - - >>> native(str(u'ABC')) - u'ABC' - >>> type(native(str(u'ABC'))) - unicode - - >>> native(bytes(b'ABC')) - b'ABC' - >>> type(native(bytes(b'ABC'))) - bytes - - >>> native(int(10**20)) - 100000000000000000000L - >>> type(native(int(10**20))) - long - - Existing native types on Py2 will be returned unchanged: - - >>> type(native(u'ABC')) - unicode - """ - if hasattr(obj, '__native__'): - return obj.__native__() - else: - return obj - - -# Implementation of exec_ is from ``six``: -if PY3: - import builtins - exec_ = getattr(builtins, "exec") -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""") - - -# Defined here for backward compatibility: -def old_div(a, b): - """ - DEPRECATED: import ``old_div`` from ``past.utils`` instead. - - Equivalent to ``a / b`` on Python 2 without ``from __future__ import - division``. - - TODO: generalize this to other objects (like arrays etc.) - """ - if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral): - return a // b - else: - return a / b - - -def as_native_str(encoding='utf-8'): - ''' - A decorator to turn a function or method call that returns text, i.e. - unicode, into one that returns a native platform str. - - Use it as a decorator like this:: - - from __future__ import unicode_literals - - class MyClass(object): - @as_native_str(encoding='ascii') - def __repr__(self): - return next(self._iter).upper() - ''' - if PY3: - return lambda f: f - else: - def encoder(f): - @functools.wraps(f) - def wrapper(*args, **kwargs): - return f(*args, **kwargs).encode(encoding=encoding) - return wrapper - return encoder - -# listvalues and listitems definitions from Nick Coghlan's (withdrawn) -# PEP 496: -try: - dict.iteritems -except AttributeError: - # Python 3 - def listvalues(d): - return list(d.values()) - def listitems(d): - return list(d.items()) -else: - # Python 2 - def listvalues(d): - return d.values() - def listitems(d): - return d.items() - -if PY3: - def ensure_new_type(obj): - return obj -else: - def ensure_new_type(obj): - from future.types.newbytes import newbytes - from future.types.newstr import newstr - from future.types.newint import newint - from future.types.newdict import newdict - - native_type = type(native(obj)) - - # Upcast only if the type is already a native (non-future) type - if issubclass(native_type, type(obj)): - # Upcast - if native_type == str: # i.e. Py2 8-bit str - return newbytes(obj) - elif native_type == unicode: - return newstr(obj) - elif native_type == int: - return newint(obj) - elif native_type == long: - return newint(obj) - elif native_type == dict: - return newdict(obj) - else: - return obj - else: - # Already a new type - assert type(obj) in [newbytes, newstr] - return obj - - -__all__ = ['PY2', 'PY26', 'PY3', 'PYPY', - 'as_native_str', 'bind_method', 'bord', 'bstr', - 'bytes_to_native_str', 'encode_filename', 'ensure_new_type', - 'exec_', 'get_next', 'getexception', 'implements_iterator', - 'is_new_style', 'isbytes', 'isidentifier', 'isint', - 'isnewbytes', 'istext', 'iteritems', 'iterkeys', 'itervalues', - 'lfilter', 'listitems', 'listvalues', 'lmap', 'lrange', - 'lzip', 'native', 'native_bytes', 'native_str', - 'native_str_to_bytes', 'old_div', - 'python_2_unicode_compatible', 'raise_', - 'raise_with_traceback', 'reraise', 'text_to_native_str', - 'tobytes', 'viewitems', 'viewkeys', 'viewvalues', - 'with_metaclass' - ] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/utils/surrogateescape.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/utils/surrogateescape.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/future/utils/surrogateescape.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/future/utils/surrogateescape.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,200 +0,0 @@ -""" -This is Victor Stinner's pure-Python implementation of PEP 383: the "surrogateescape" error -handler of Python 3. - -Source: misc/python/surrogateescape.py in https://bitbucket.org/haypo/misc -""" - -# This code is released under the Python license and the BSD 2-clause license - -import codecs -import sys - -from future import utils - - -FS_ERRORS = 'surrogateescape' - -# # -- Python 2/3 compatibility ------------------------------------- -# FS_ERRORS = 'my_surrogateescape' - -def u(text): - if utils.PY3: - return text - else: - return text.decode('unicode_escape') - -def b(data): - if utils.PY3: - return data.encode('latin1') - else: - return data - -if utils.PY3: - _unichr = chr - bytes_chr = lambda code: bytes((code,)) -else: - _unichr = unichr - bytes_chr = chr - -def surrogateescape_handler(exc): - """ - Pure Python implementation of the PEP 383: the "surrogateescape" error - handler of Python 3. Undecodable bytes will be replaced by a Unicode - character U+DCxx on decoding, and these are translated into the - original bytes on encoding. - """ - mystring = exc.object[exc.start:exc.end] - - try: - if isinstance(exc, UnicodeDecodeError): - # mystring is a byte-string in this case - decoded = replace_surrogate_decode(mystring) - elif isinstance(exc, UnicodeEncodeError): - # In the case of u'\udcc3'.encode('ascii', - # 'this_surrogateescape_handler'), both Python 2.x and 3.x raise an - # exception anyway after this function is called, even though I think - # it's doing what it should. It seems that the strict encoder is called - # to encode the unicode string that this function returns ... - decoded = replace_surrogate_encode(mystring) - else: - raise exc - except NotASurrogateError: - raise exc - return (decoded, exc.end) - - -class NotASurrogateError(Exception): - pass - - -def replace_surrogate_encode(mystring): - """ - Returns a (unicode) string, not the more logical bytes, because the codecs - register_error functionality expects this. - """ - decoded = [] - for ch in mystring: - # if utils.PY3: - # code = ch - # else: - code = ord(ch) - - # The following magic comes from Py3.3's Python/codecs.c file: - if not 0xD800 <= code <= 0xDCFF: - # Not a surrogate. Fail with the original exception. - raise NotASurrogateError - # mybytes = [0xe0 | (code >> 12), - # 0x80 | ((code >> 6) & 0x3f), - # 0x80 | (code & 0x3f)] - # Is this a good idea? - if 0xDC00 <= code <= 0xDC7F: - decoded.append(_unichr(code - 0xDC00)) - elif code <= 0xDCFF: - decoded.append(_unichr(code - 0xDC00)) - else: - raise NotASurrogateError - return str().join(decoded) - - -def replace_surrogate_decode(mybytes): - """ - Returns a (unicode) string - """ - decoded = [] - for ch in mybytes: - # We may be parsing newbytes (in which case ch is an int) or a native - # str on Py2 - if isinstance(ch, int): - code = ch - else: - code = ord(ch) - if 0x80 <= code <= 0xFF: - decoded.append(_unichr(0xDC00 + code)) - elif code <= 0x7F: - decoded.append(_unichr(code)) - else: - # # It may be a bad byte - # # Try swallowing it. - # continue - # print("RAISE!") - raise NotASurrogateError - return str().join(decoded) - - -def encodefilename(fn): - if FS_ENCODING == 'ascii': - # ASCII encoder of Python 2 expects that the error handler returns a - # Unicode string encodable to ASCII, whereas our surrogateescape error - # handler has to return bytes in 0x80-0xFF range. - encoded = [] - for index, ch in enumerate(fn): - code = ord(ch) - if code < 128: - ch = bytes_chr(code) - elif 0xDC80 <= code <= 0xDCFF: - ch = bytes_chr(code - 0xDC00) - else: - raise UnicodeEncodeError(FS_ENCODING, - fn, index, index+1, - 'ordinal not in range(128)') - encoded.append(ch) - return bytes().join(encoded) - elif FS_ENCODING == 'utf-8': - # UTF-8 encoder of Python 2 encodes surrogates, so U+DC80-U+DCFF - # doesn't go through our error handler - encoded = [] - for index, ch in enumerate(fn): - code = ord(ch) - if 0xD800 <= code <= 0xDFFF: - if 0xDC80 <= code <= 0xDCFF: - ch = bytes_chr(code - 0xDC00) - encoded.append(ch) - else: - raise UnicodeEncodeError( - FS_ENCODING, - fn, index, index+1, 'surrogates not allowed') - else: - ch_utf8 = ch.encode('utf-8') - encoded.append(ch_utf8) - return bytes().join(encoded) - else: - return fn.encode(FS_ENCODING, FS_ERRORS) - -def decodefilename(fn): - return fn.decode(FS_ENCODING, FS_ERRORS) - -FS_ENCODING = 'ascii'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]') -# FS_ENCODING = 'cp932'; fn = b('[abc\x81\x00]'); encoded = u('[abc\udc81\x00]') -# FS_ENCODING = 'UTF-8'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]') - - -# normalize the filesystem encoding name. -# For example, we expect "utf-8", not "UTF8". -FS_ENCODING = codecs.lookup(FS_ENCODING).name - - -def register_surrogateescape(): - """ - Registers the surrogateescape error handler on Python 2 (only) - """ - if utils.PY3: - return - try: - codecs.lookup_error(FS_ERRORS) - except LookupError: - codecs.register_error(FS_ERRORS, surrogateescape_handler) - - -if __name__ == '__main__': - pass - # # Tests: - # register_surrogateescape() - - # b = decodefilename(fn) - # assert b == encoded, "%r != %r" % (b, encoded) - # c = encodefilename(b) - # assert c == fn, '%r != %r' % (c, fn) - # # print("ok") - - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixer_util.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixer_util.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixer_util.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixer_util.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,518 +0,0 @@ -""" -Utility functions from 2to3, 3to2 and python-modernize (and some home-grown -ones). - -Licences: -2to3: PSF License v2 -3to2: Apache Software License (from 3to2/setup.py) -python-modernize licence: BSD (from python-modernize/LICENSE) -""" - -from lib2to3.fixer_util import (FromImport, Newline, is_import, - find_root, does_tree_import, Comma) -from lib2to3.pytree import Leaf, Node -from lib2to3.pygram import python_symbols as syms, python_grammar -from lib2to3.pygram import token -from lib2to3.fixer_util import (Node, Call, Name, syms, Comma, Number) -import re - - -def canonical_fix_name(fix, avail_fixes): - """ - Examples: - >>> canonical_fix_name('fix_wrap_text_literals') - 'libfuturize.fixes.fix_wrap_text_literals' - >>> canonical_fix_name('wrap_text_literals') - 'libfuturize.fixes.fix_wrap_text_literals' - >>> canonical_fix_name('wrap_te') - ValueError("unknown fixer name") - >>> canonical_fix_name('wrap') - ValueError("ambiguous fixer name") - """ - if ".fix_" in fix: - return fix - else: - if fix.startswith('fix_'): - fix = fix[4:] - # Infer the full module name for the fixer. - # First ensure that no names clash (e.g. - # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah): - found = [f for f in avail_fixes - if f.endswith('fix_{0}'.format(fix))] - if len(found) > 1: - raise ValueError("Ambiguous fixer name. Choose a fully qualified " - "module name instead from these:\n" + - "\n".join(" " + myf for myf in found)) - elif len(found) == 0: - raise ValueError("Unknown fixer. Use --list-fixes or -l for a list.") - return found[0] - - - -## These functions are from 3to2 by Joe Amenta: - -def Star(prefix=None): - return Leaf(token.STAR, u'*', prefix=prefix) - -def DoubleStar(prefix=None): - return Leaf(token.DOUBLESTAR, u'**', prefix=prefix) - -def Minus(prefix=None): - return Leaf(token.MINUS, u'-', prefix=prefix) - -def commatize(leafs): - """ - Accepts/turns: (Name, Name, ..., Name, Name) - Returns/into: (Name, Comma, Name, Comma, ..., Name, Comma, Name) - """ - new_leafs = [] - for leaf in leafs: - new_leafs.append(leaf) - new_leafs.append(Comma()) - del new_leafs[-1] - return new_leafs - -def indentation(node): - """ - Returns the indentation for this node - Iff a node is in a suite, then it has indentation. - """ - while node.parent is not None and node.parent.type != syms.suite: - node = node.parent - if node.parent is None: - return u"" - # The first three children of a suite are NEWLINE, INDENT, (some other node) - # INDENT.value contains the indentation for this suite - # anything after (some other node) has the indentation as its prefix. - if node.type == token.INDENT: - return node.value - elif node.prev_sibling is not None and node.prev_sibling.type == token.INDENT: - return node.prev_sibling.value - elif node.prev_sibling is None: - return u"" - else: - return node.prefix - -def indentation_step(node): - """ - Dirty little trick to get the difference between each indentation level - Implemented by finding the shortest indentation string - (technically, the "least" of all of the indentation strings, but - tabs and spaces mixed won't get this far, so those are synonymous.) - """ - r = find_root(node) - # Collect all indentations into one set. - all_indents = set(i.value for i in r.pre_order() if i.type == token.INDENT) - if not all_indents: - # nothing is indented anywhere, so we get to pick what we want - return u" " # four spaces is a popular convention - else: - return min(all_indents) - -def suitify(parent): - """ - Turn the stuff after the first colon in parent's children - into a suite, if it wasn't already - """ - for node in parent.children: - if node.type == syms.suite: - # already in the prefered format, do nothing - return - - # One-liners have no suite node, we have to fake one up - for i, node in enumerate(parent.children): - if node.type == token.COLON: - break - else: - raise ValueError(u"No class suite and no ':'!") - # Move everything into a suite node - suite = Node(syms.suite, [Newline(), Leaf(token.INDENT, indentation(node) + indentation_step(node))]) - one_node = parent.children[i+1] - one_node.remove() - one_node.prefix = u'' - suite.append_child(one_node) - parent.append_child(suite) - -def NameImport(package, as_name=None, prefix=None): - """ - Accepts a package (Name node), name to import it as (string), and - optional prefix and returns a node: - import [as ] - """ - if prefix is None: - prefix = u"" - children = [Name(u"import", prefix=prefix), package] - if as_name is not None: - children.extend([Name(u"as", prefix=u" "), - Name(as_name, prefix=u" ")]) - return Node(syms.import_name, children) - -_compound_stmts = (syms.if_stmt, syms.while_stmt, syms.for_stmt, syms.try_stmt, syms.with_stmt) -_import_stmts = (syms.import_name, syms.import_from) - -def import_binding_scope(node): - """ - Generator yields all nodes for which a node (an import_stmt) has scope - The purpose of this is for a call to _find() on each of them - """ - # import_name / import_from are small_stmts - assert node.type in _import_stmts - test = node.next_sibling - # A small_stmt can only be followed by a SEMI or a NEWLINE. - while test.type == token.SEMI: - nxt = test.next_sibling - # A SEMI can only be followed by a small_stmt or a NEWLINE - if nxt.type == token.NEWLINE: - break - else: - yield nxt - # A small_stmt can only be followed by either a SEMI or a NEWLINE - test = nxt.next_sibling - # Covered all subsequent small_stmts after the import_stmt - # Now to cover all subsequent stmts after the parent simple_stmt - parent = node.parent - assert parent.type == syms.simple_stmt - test = parent.next_sibling - while test is not None: - # Yes, this will yield NEWLINE and DEDENT. Deal with it. - yield test - test = test.next_sibling - - context = parent.parent - # Recursively yield nodes following imports inside of a if/while/for/try/with statement - if context.type in _compound_stmts: - # import is in a one-liner - c = context - while c.next_sibling is not None: - yield c.next_sibling - c = c.next_sibling - context = context.parent - - # Can't chain one-liners on one line, so that takes care of that. - - p = context.parent - if p is None: - return - - # in a multi-line suite - - while p.type in _compound_stmts: - - if context.type == syms.suite: - yield context - - context = context.next_sibling - - if context is None: - context = p.parent - p = context.parent - if p is None: - break - -def ImportAsName(name, as_name, prefix=None): - new_name = Name(name) - new_as = Name(u"as", prefix=u" ") - new_as_name = Name(as_name, prefix=u" ") - new_node = Node(syms.import_as_name, [new_name, new_as, new_as_name]) - if prefix is not None: - new_node.prefix = prefix - return new_node - - -def is_docstring(node): - """ - Returns True if the node appears to be a docstring - """ - return (node.type == syms.simple_stmt and - len(node.children) > 0 and node.children[0].type == token.STRING) - - -def future_import(feature, node): - """ - This seems to work - """ - root = find_root(node) - - if does_tree_import(u"__future__", feature, node): - return - - # Look for a shebang or encoding line - shebang_encoding_idx = None - - for idx, node in enumerate(root.children): - # Is it a shebang or encoding line? - if is_shebang_comment(node) or is_encoding_comment(node): - shebang_encoding_idx = idx - if is_docstring(node): - # skip over docstring - continue - names = check_future_import(node) - if not names: - # not a future statement; need to insert before this - break - if feature in names: - # already imported - return - - import_ = FromImport(u'__future__', [Leaf(token.NAME, feature, prefix=" ")]) - if shebang_encoding_idx == 0 and idx == 0: - # If this __future__ import would go on the first line, - # detach the shebang / encoding prefix from the current first line. - # and attach it to our new __future__ import node. - import_.prefix = root.children[0].prefix - root.children[0].prefix = u'' - # End the __future__ import line with a newline and add a blank line - # afterwards: - children = [import_ , Newline()] - root.insert_child(idx, Node(syms.simple_stmt, children)) - - -def future_import2(feature, node): - """ - An alternative to future_import() which might not work ... - """ - root = find_root(node) - - if does_tree_import(u"__future__", feature, node): - return - - insert_pos = 0 - for idx, node in enumerate(root.children): - if node.type == syms.simple_stmt and node.children and \ - node.children[0].type == token.STRING: - insert_pos = idx + 1 - break - - for thing_after in root.children[insert_pos:]: - if thing_after.type == token.NEWLINE: - insert_pos += 1 - continue - - prefix = thing_after.prefix - thing_after.prefix = u"" - break - else: - prefix = u"" - - import_ = FromImport(u"__future__", [Leaf(token.NAME, feature, prefix=u" ")]) - - children = [import_, Newline()] - root.insert_child(insert_pos, Node(syms.simple_stmt, children, prefix=prefix)) - -def parse_args(arglist, scheme): - u""" - Parse a list of arguments into a dict - """ - arglist = [i for i in arglist if i.type != token.COMMA] - - ret_mapping = dict([(k, None) for k in scheme]) - - for i, arg in enumerate(arglist): - if arg.type == syms.argument and arg.children[1].type == token.EQUAL: - # argument < NAME '=' any > - slot = arg.children[0].value - ret_mapping[slot] = arg.children[2] - else: - slot = scheme[i] - ret_mapping[slot] = arg - - return ret_mapping - - -# def is_import_from(node): -# """Returns true if the node is a statement "from ... import ..." -# """ -# return node.type == syms.import_from - - -def is_import_stmt(node): - return (node.type == syms.simple_stmt and node.children and - is_import(node.children[0])) - - -def touch_import_top(package, name_to_import, node): - """Works like `does_tree_import` but adds an import statement at the - top if it was not imported (but below any __future__ imports) and below any - comments such as shebang lines). - - Based on lib2to3.fixer_util.touch_import() - - Calling this multiple times adds the imports in reverse order. - - Also adds "standard_library.install_aliases()" after "from future import - standard_library". This should probably be factored into another function. - """ - - root = find_root(node) - - if does_tree_import(package, name_to_import, root): - return - - # Ideally, we would look for whether futurize --all-imports has been run, - # as indicated by the presence of ``from builtins import (ascii, ..., - # zip)`` -- and, if it has, we wouldn't import the name again. - - # Look for __future__ imports and insert below them - found = False - for name in ['absolute_import', 'division', 'print_function', - 'unicode_literals']: - if does_tree_import('__future__', name, root): - found = True - break - if found: - # At least one __future__ import. We want to loop until we've seen them - # all. - start, end = None, None - for idx, node in enumerate(root.children): - if check_future_import(node): - start = idx - # Start looping - idx2 = start - while node: - node = node.next_sibling - idx2 += 1 - if not check_future_import(node): - end = idx2 - break - break - assert start is not None - assert end is not None - insert_pos = end - else: - # No __future__ imports. - # We look for a docstring and insert the new node below that. If no docstring - # exists, just insert the node at the top. - for idx, node in enumerate(root.children): - if node.type != syms.simple_stmt: - break - if not is_docstring(node): - # This is the usual case. - break - insert_pos = idx - - if package is None: - import_ = Node(syms.import_name, [ - Leaf(token.NAME, u"import"), - Leaf(token.NAME, name_to_import, prefix=u" ") - ]) - else: - import_ = FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")]) - if name_to_import == u'standard_library': - # Add: - # standard_library.install_aliases() - # after: - # from future import standard_library - install_hooks = Node(syms.simple_stmt, - [Node(syms.power, - [Leaf(token.NAME, u'standard_library'), - Node(syms.trailer, [Leaf(token.DOT, u'.'), - Leaf(token.NAME, u'install_aliases')]), - Node(syms.trailer, [Leaf(token.LPAR, u'('), - Leaf(token.RPAR, u')')]) - ]) - ] - ) - children_hooks = [install_hooks, Newline()] - else: - children_hooks = [] - - # FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")]) - - children_import = [import_, Newline()] - old_prefix = root.children[insert_pos].prefix - root.children[insert_pos].prefix = u'' - root.insert_child(insert_pos, Node(syms.simple_stmt, children_import, prefix=old_prefix)) - if len(children_hooks) > 0: - root.insert_child(insert_pos + 1, Node(syms.simple_stmt, children_hooks)) - - -## The following functions are from python-modernize by Armin Ronacher: -# (a little edited). - -def check_future_import(node): - """If this is a future import, return set of symbols that are imported, - else return None.""" - # node should be the import statement here - savenode = node - if not (node.type == syms.simple_stmt and node.children): - return set() - node = node.children[0] - # now node is the import_from node - if not (node.type == syms.import_from and - # node.type == token.NAME and # seems to break it - hasattr(node.children[1], 'value') and - node.children[1].value == u'__future__'): - return set() - node = node.children[3] - # now node is the import_as_name[s] - # print(python_grammar.number2symbol[node.type]) # breaks sometimes - if node.type == syms.import_as_names: - result = set() - for n in node.children: - if n.type == token.NAME: - result.add(n.value) - elif n.type == syms.import_as_name: - n = n.children[0] - assert n.type == token.NAME - result.add(n.value) - return result - elif node.type == syms.import_as_name: - node = node.children[0] - assert node.type == token.NAME - return set([node.value]) - elif node.type == token.NAME: - return set([node.value]) - else: - # TODO: handle brackets like this: - # from __future__ import (absolute_import, division) - assert False, "strange import: %s" % savenode - - -SHEBANG_REGEX = r'^#!.*python' -ENCODING_REGEX = r"^#.*coding[:=]\s*([-\w.]+)" - - -def is_shebang_comment(node): - """ - Comments are prefixes for Leaf nodes. Returns whether the given node has a - prefix that looks like a shebang line or an encoding line: - - #!/usr/bin/env python - #!/usr/bin/python3 - """ - return bool(re.match(SHEBANG_REGEX, node.prefix)) - - -def is_encoding_comment(node): - """ - Comments are prefixes for Leaf nodes. Returns whether the given node has a - prefix that looks like an encoding line: - - # coding: utf-8 - # encoding: utf-8 - # -*- coding: -*- - # vim: set fileencoding= : - """ - return bool(re.match(ENCODING_REGEX, node.prefix)) - - -def wrap_in_fn_call(fn_name, args, prefix=None): - """ - Example: - >>> wrap_in_fn_call("oldstr", (arg,)) - oldstr(arg) - - >>> wrap_in_fn_call("olddiv", (arg1, arg2)) - olddiv(arg1, arg2) - """ - assert len(args) > 0 - if len(args) == 1: - newargs = args - elif len(args) == 2: - expr1, expr2 = args - newargs = [expr1, Comma(), expr2] - else: - assert NotImplementedError('write me') - return Call(Name(fn_name), newargs, prefix=prefix) - - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -""" -Fixer for import statements, with a __future__ import line. - -Based on lib2to3/fixes/fix_import.py, but extended slightly so it also -supports Cython modules. - -If spam is being imported from the local directory, this import: - from spam import eggs -becomes: - from __future__ import absolute_import - from .spam import eggs - -and this import: - import spam -becomes: - from __future__ import absolute_import - from . import spam -""" - -from os.path import dirname, join, exists, sep -from lib2to3.fixes.fix_import import FixImport -from lib2to3.fixer_util import FromImport, syms -from lib2to3.fixes.fix_import import traverse_imports - -from libfuturize.fixer_util import future_import - - -class FixAbsoluteImport(FixImport): - run_order = 9 - - def transform(self, node, results): - """ - Copied from FixImport.transform(), but with this line added in - any modules that had implicit relative imports changed: - - from __future__ import absolute_import" - """ - if self.skip: - return - imp = results['imp'] - - if node.type == syms.import_from: - # Some imps are top-level (eg: 'import ham') - # some are first level (eg: 'import ham.eggs') - # some are third level (eg: 'import ham.eggs as spam') - # Hence, the loop - while not hasattr(imp, 'value'): - imp = imp.children[0] - if self.probably_a_local_import(imp.value): - imp.value = u"." + imp.value - imp.changed() - future_import(u"absolute_import", node) - else: - have_local = False - have_absolute = False - for mod_name in traverse_imports(imp): - if self.probably_a_local_import(mod_name): - have_local = True - else: - have_absolute = True - if have_absolute: - if have_local: - # We won't handle both sibling and absolute imports in the - # same statement at the moment. - self.warning(node, "absolute and local imports together") - return - - new = FromImport(u".", [imp]) - new.prefix = node.prefix - future_import(u"absolute_import", node) - return new - - def probably_a_local_import(self, imp_name): - """ - Like the corresponding method in the base class, but this also - supports Cython modules. - """ - if imp_name.startswith(u"."): - # Relative imports are certainly not local imports. - return False - imp_name = imp_name.split(u".", 1)[0] - base_path = dirname(self.filename) - base_path = join(base_path, imp_name) - # If there is no __init__.py next to the file its not in a package - # so can't be a relative import. - if not exists(join(dirname(base_path), "__init__.py")): - return False - for ext in [".py", sep, ".pyc", ".so", ".sl", ".pyd", ".pyx"]: - if exists(base_path + ext): - return True - return False - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -""" -Fixer for adding: - - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - -This is "stage 1": hopefully uncontroversial changes. - -Stage 2 adds ``unicode_literals``. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixAddFutureImportsExceptUnicodeLiterals(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - - run_order = 9 - - def transform(self, node, results): - # Reverse order: - future_import(u"print_function", node) - future_import(u"division", node) - future_import(u"absolute_import", node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -""" -Fixer that adds ``from past.builtins import basestring`` if there is a -reference to ``basestring`` -""" - -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -class FixBasestring(fixer_base.BaseFix): - BM_compatible = True - - PATTERN = "'basestring'" - - def transform(self, node, results): - touch_import_top(u'past.builtins', 'basestring', node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -"""Optional fixer that changes all unprefixed string literals "..." to b"...". - -br'abcd' is a SyntaxError on Python 2 but valid on Python 3. -ur'abcd' is a SyntaxError on Python 3 but valid on Python 2. - -""" -from __future__ import unicode_literals - -import re -from lib2to3.pgen2 import token -from lib2to3 import fixer_base - -_literal_re = re.compile(r"[^bBuUrR]?[\'\"]") - -class FixBytes(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "STRING" - - def transform(self, node, results): - if node.type == token.STRING: - if _literal_re.match(node.value): - new = node.clone() - new.value = u'b' + new.value - return new diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -# coding: utf-8 -""" -Fixer for the cmp() function on Py2, which was removed in Py3. - -Adds this import line:: - - from past.builtins import cmp - -if cmp() is called in the code. -""" - -from __future__ import unicode_literals -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -expression = "name='cmp'" - - -class FixCmp(fixer_base.BaseFix): - BM_compatible = True - run_order = 9 - - PATTERN = """ - power< - ({0}) trailer< '(' args=[any] ')' > - rest=any* > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'past.builtins', name.value, node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -""" -UNFINISHED -For the ``future`` package. - -Adds this import line: - - from __future__ import division - -at the top so the code runs identically on Py3 and Py2.6/2.7 -""" - -from libpasteurize.fixes.fix_division import FixDivision - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line: - - from __future__ import division - -at the top and changes any old-style divisions to be calls to -past.utils.old_div so the code runs as before on Py2.6/2.7 and has the same -behaviour on Py3. - -If "from __future__ import division" is already in effect, this fixer does -nothing. -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import syms, does_tree_import -from libfuturize.fixer_util import (token, future_import, touch_import_top, - wrap_in_fn_call) - - -def match_division(node): - u""" - __future__.division redefines the meaning of a single slash for division, - so we match that and only that. - """ - slash = token.SLASH - return node.type == slash and not node.next_sibling.type == slash and \ - not node.prev_sibling.type == slash - - -class FixDivisionSafe(fixer_base.BaseFix): - # BM_compatible = True - run_order = 4 # this seems to be ignored? - - _accept_type = token.SLASH - - PATTERN = """ - term<(not('/') any)+ '/' ((not('/') any))> - """ - - def start_tree(self, tree, name): - """ - Skip this fixer if "__future__.division" is already imported. - """ - super(FixDivisionSafe, self).start_tree(tree, name) - self.skip = "division" in tree.future_features - - def match(self, node): - u""" - Since the tree needs to be fixed once and only once if and only if it - matches, we can start discarding matches after the first. - """ - if (node.type == self.syms.term and - len(node.children) == 3 and - match_division(node.children[1])): - expr1, expr2 = node.children[0], node.children[2] - return expr1, expr2 - else: - return False - - def transform(self, node, results): - if self.skip: - return - future_import(u"division", node) - - touch_import_top(u'past.utils', u'old_div', node) - expr1, expr2 = results[0].clone(), results[1].clone() - # Strip any leading space for the first number: - expr1.prefix = u'' - return wrap_in_fn_call("old_div", (expr1, expr2), prefix=node.prefix) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -# coding: utf-8 -""" -Fixer for the execfile() function on Py2, which was removed in Py3. - -The Lib/lib2to3/fixes/fix_execfile.py module has some problems: see -python-future issue #37. This fixer merely imports execfile() from -past.builtins and leaves the code alone. - -Adds this import line:: - - from past.builtins import execfile - -for the function execfile() that was removed from Py3. -""" - -from __future__ import unicode_literals -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -expression = "name='execfile'" - - -class FixExecfile(fixer_base.BaseFix): - BM_compatible = True - run_order = 9 - - PATTERN = """ - power< - ({0}) trailer< '(' args=[any] ')' > - rest=any* > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'past.builtins', name.value, node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line:: - - from builtins import XYZ - -for each of the functions XYZ that is used in the module. - -Adds these imports after any other imports (in an initial block of them). -""" - -from __future__ import unicode_literals - -from lib2to3 import fixer_base -from lib2to3.pygram import python_symbols as syms -from lib2to3.fixer_util import Name, Call, in_special_context - -from libfuturize.fixer_util import touch_import_top - -# All builtins are: -# from future.builtins.iterators import (filter, map, zip) -# from future.builtins.misc import (ascii, chr, hex, input, isinstance, oct, open, round, super) -# from future.types import (bytes, dict, int, range, str) -# We don't need isinstance any more. - -replaced_builtin_fns = '''filter map zip - ascii chr hex input next oct - bytes range str raw_input'''.split() - # This includes raw_input as a workaround for the - # lib2to3 fixer for raw_input on Py3 (only), allowing - # the correct import to be included. (Py3 seems to run - # the fixers the wrong way around, perhaps ignoring the - # run_order class attribute below ...) - -expression = '|'.join(["name='{0}'".format(name) for name in replaced_builtin_fns]) - - -class FixFutureBuiltins(fixer_base.BaseFix): - BM_compatible = True - run_order = 7 - - # Currently we only match uses as a function. This doesn't match e.g.: - # if isinstance(s, str): - # ... - PATTERN = """ - power< - ({0}) trailer< '(' [arglist=any] ')' > - rest=any* > - | - power< - 'map' trailer< '(' [arglist=any] ')' > - > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'builtins', name.value, node) - # name.replace(Name(u"input", prefix=name.prefix)) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -""" -For the ``future`` package. - -Changes any imports needed to reflect the standard library reorganization. Also -Also adds these import lines: - - from future import standard_library - standard_library.install_aliases() - -after any __future__ imports but before any other imports. -""" - -from lib2to3.fixes.fix_imports import FixImports -from libfuturize.fixer_util import touch_import_top - - -class FixFutureStandardLibrary(FixImports): - run_order = 8 - - def transform(self, node, results): - result = super(FixFutureStandardLibrary, self).transform(node, results) - # TODO: add a blank line between any __future__ imports and this? - touch_import_top(u'future', u'standard_library', node) - return result - - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -""" -For the ``future`` package. - -A special fixer that ensures that these lines have been added:: - - from future import standard_library - standard_library.install_hooks() - -even if the only module imported was ``urllib``, in which case the regular fixer -wouldn't have added these lines. - -""" - -from lib2to3.fixes.fix_urllib import FixUrllib -from libfuturize.fixer_util import touch_import_top, find_root - - -class FixFutureStandardLibraryUrllib(FixUrllib): # not a subclass of FixImports - run_order = 8 - - def transform(self, node, results): - # transform_member() in lib2to3/fixes/fix_urllib.py breaks node so find_root(node) - # no longer works after the super() call below. So we find the root first: - root = find_root(node) - result = super(FixFutureStandardLibraryUrllib, self).transform(node, results) - # TODO: add a blank line between any __future__ imports and this? - touch_import_top(u'future', u'standard_library', root) - return result - - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,262 +0,0 @@ -# coding: utf-8 -"""Fixer for __metaclass__ = X -> (future.utils.with_metaclass(X)) methods. - - The various forms of classef (inherits nothing, inherits once, inherints - many) don't parse the same in the CST so we look at ALL classes for - a __metaclass__ and if we find one normalize the inherits to all be - an arglist. - - For one-liner classes ('class X: pass') there is no indent/dedent so - we normalize those into having a suite. - - Moving the __metaclass__ into the classdef can also cause the class - body to be empty so there is some special casing for that as well. - - This fixer also tries very hard to keep original indenting and spacing - in all those corner cases. -""" -# This is a derived work of Lib/lib2to3/fixes/fix_metaclass.py under the -# copyright of the Python Software Foundation, licensed under the Python -# Software Foundation License 2. -# -# Copyright notice: -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013 Python Software Foundation. All rights reserved. -# -# Full license text: http://docs.python.org/3.4/license.html - -# Author: Jack Diederich, Daniel Neuhäuser - -# Local imports -from lib2to3 import fixer_base -from lib2to3.pygram import token -from lib2to3.fixer_util import Name, syms, Node, Leaf, touch_import, Call, \ - String, Comma, parenthesize - - -def has_metaclass(parent): - """ we have to check the cls_node without changing it. - There are two possiblities: - 1) clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta') - 2) clsdef => simple_stmt => expr_stmt => Leaf('__meta') - """ - for node in parent.children: - if node.type == syms.suite: - return has_metaclass(node) - elif node.type == syms.simple_stmt and node.children: - expr_node = node.children[0] - if expr_node.type == syms.expr_stmt and expr_node.children: - left_side = expr_node.children[0] - if isinstance(left_side, Leaf) and \ - left_side.value == '__metaclass__': - return True - return False - - -def fixup_parse_tree(cls_node): - """ one-line classes don't get a suite in the parse tree so we add - one to normalize the tree - """ - for node in cls_node.children: - if node.type == syms.suite: - # already in the preferred format, do nothing - return - - # !%@#! oneliners have no suite node, we have to fake one up - for i, node in enumerate(cls_node.children): - if node.type == token.COLON: - break - else: - raise ValueError("No class suite and no ':'!") - - # move everything into a suite node - suite = Node(syms.suite, []) - while cls_node.children[i+1:]: - move_node = cls_node.children[i+1] - suite.append_child(move_node.clone()) - move_node.remove() - cls_node.append_child(suite) - node = suite - - -def fixup_simple_stmt(parent, i, stmt_node): - """ if there is a semi-colon all the parts count as part of the same - simple_stmt. We just want the __metaclass__ part so we move - everything efter the semi-colon into its own simple_stmt node - """ - for semi_ind, node in enumerate(stmt_node.children): - if node.type == token.SEMI: # *sigh* - break - else: - return - - node.remove() # kill the semicolon - new_expr = Node(syms.expr_stmt, []) - new_stmt = Node(syms.simple_stmt, [new_expr]) - while stmt_node.children[semi_ind:]: - move_node = stmt_node.children[semi_ind] - new_expr.append_child(move_node.clone()) - move_node.remove() - parent.insert_child(i, new_stmt) - new_leaf1 = new_stmt.children[0].children[0] - old_leaf1 = stmt_node.children[0].children[0] - new_leaf1.prefix = old_leaf1.prefix - - -def remove_trailing_newline(node): - if node.children and node.children[-1].type == token.NEWLINE: - node.children[-1].remove() - - -def find_metas(cls_node): - # find the suite node (Mmm, sweet nodes) - for node in cls_node.children: - if node.type == syms.suite: - break - else: - raise ValueError("No class suite!") - - # look for simple_stmt[ expr_stmt[ Leaf('__metaclass__') ] ] - for i, simple_node in list(enumerate(node.children)): - if simple_node.type == syms.simple_stmt and simple_node.children: - expr_node = simple_node.children[0] - if expr_node.type == syms.expr_stmt and expr_node.children: - # Check if the expr_node is a simple assignment. - left_node = expr_node.children[0] - if isinstance(left_node, Leaf) and \ - left_node.value == u'__metaclass__': - # We found a assignment to __metaclass__. - fixup_simple_stmt(node, i, simple_node) - remove_trailing_newline(simple_node) - yield (node, i, simple_node) - - -def fixup_indent(suite): - """ If an INDENT is followed by a thing with a prefix then nuke the prefix - Otherwise we get in trouble when removing __metaclass__ at suite start - """ - kids = suite.children[::-1] - # find the first indent - while kids: - node = kids.pop() - if node.type == token.INDENT: - break - - # find the first Leaf - while kids: - node = kids.pop() - if isinstance(node, Leaf) and node.type != token.DEDENT: - if node.prefix: - node.prefix = u'' - return - else: - kids.extend(node.children[::-1]) - - -class FixMetaclass(fixer_base.BaseFix): - BM_compatible = True - - PATTERN = """ - classdef - """ - - def transform(self, node, results): - if not has_metaclass(node): - return - - fixup_parse_tree(node) - - # find metaclasses, keep the last one - last_metaclass = None - for suite, i, stmt in find_metas(node): - last_metaclass = stmt - stmt.remove() - - text_type = node.children[0].type # always Leaf(nnn, 'class') - - # figure out what kind of classdef we have - if len(node.children) == 7: - # Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite]) - # 0 1 2 3 4 5 6 - if node.children[3].type == syms.arglist: - arglist = node.children[3] - # Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite]) - else: - parent = node.children[3].clone() - arglist = Node(syms.arglist, [parent]) - node.set_child(3, arglist) - elif len(node.children) == 6: - # Node(classdef, ['class', 'name', '(', ')', ':', suite]) - # 0 1 2 3 4 5 - arglist = Node(syms.arglist, []) - node.insert_child(3, arglist) - elif len(node.children) == 4: - # Node(classdef, ['class', 'name', ':', suite]) - # 0 1 2 3 - arglist = Node(syms.arglist, []) - node.insert_child(2, Leaf(token.RPAR, u')')) - node.insert_child(2, arglist) - node.insert_child(2, Leaf(token.LPAR, u'(')) - else: - raise ValueError("Unexpected class definition") - - # now stick the metaclass in the arglist - meta_txt = last_metaclass.children[0].children[0] - meta_txt.value = 'metaclass' - orig_meta_prefix = meta_txt.prefix - - # Was: touch_import(None, u'future.utils', node) - touch_import(u'future.utils', u'with_metaclass', node) - - metaclass = last_metaclass.children[0].children[2].clone() - metaclass.prefix = u'' - - arguments = [metaclass] - - if arglist.children: - if len(arglist.children) == 1: - base = arglist.children[0].clone() - base.prefix = u' ' - else: - # Unfortunately six.with_metaclass() only allows one base - # class, so we have to dynamically generate a base class if - # there is more than one. - bases = parenthesize(arglist.clone()) - bases.prefix = u' ' - base = Call(Name('type'), [ - String("'NewBase'"), - Comma(), - bases, - Comma(), - Node( - syms.atom, - [Leaf(token.LBRACE, u'{'), Leaf(token.RBRACE, u'}')], - prefix=u' ' - ) - ], prefix=u' ') - arguments.extend([Comma(), base]) - - arglist.replace(Call( - Name(u'with_metaclass', prefix=arglist.prefix), - arguments - )) - - fixup_indent(suite) - - # check for empty suite - if not suite.children: - # one-liner that was just __metaclass_ - suite.remove() - pass_leaf = Leaf(text_type, u'pass') - pass_leaf.prefix = orig_meta_prefix - node.append_child(pass_leaf) - node.append_child(Leaf(token.NEWLINE, u'\n')) - - elif len(suite.children) > 1 and \ - (suite.children[-2].type == token.INDENT and - suite.children[-1].type == token.DEDENT): - # there was only one line in the class body and it was __metaclass__ - pass_leaf = Leaf(text_type, u'pass') - suite.insert_child(-1, pass_leaf) - suite.insert_child(-1, Leaf(token.NEWLINE, u'\n')) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -""" -Based on fix_next.py by Collin Winter. - -Replaces it.next() -> next(it), per PEP 3114. - -Unlike fix_next.py, this fixer doesn't replace the name of a next method with __next__, -which would break Python 2 compatibility without further help from fixers in -stage 2. -""" - -# Local imports -from lib2to3.pgen2 import token -from lib2to3.pygram import python_symbols as syms -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, Call, find_binding - -bind_warning = "Calls to builtin next() possibly shadowed by global binding" - - -class FixNextCall(fixer_base.BaseFix): - BM_compatible = True - PATTERN = """ - power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > > - | - power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > > - | - global=global_stmt< 'global' any* 'next' any* > - """ - - order = "pre" # Pre-order tree traversal - - def start_tree(self, tree, filename): - super(FixNextCall, self).start_tree(tree, filename) - - n = find_binding('next', tree) - if n: - self.warning(n, bind_warning) - self.shadowed_next = True - else: - self.shadowed_next = False - - def transform(self, node, results): - assert results - - base = results.get("base") - attr = results.get("attr") - name = results.get("name") - - if base: - if self.shadowed_next: - # Omit this: - # attr.replace(Name("__next__", prefix=attr.prefix)) - pass - else: - base = [n.clone() for n in base] - base[0].prefix = "" - node.replace(Call(Name("next", prefix=node.prefix), base)) - elif name: - # Omit this: - # n = Name("__next__", prefix=name.prefix) - # name.replace(n) - pass - elif attr: - # We don't do this transformation if we're assigning to "x.next". - # Unfortunately, it doesn't seem possible to do this in PATTERN, - # so it's being done here. - if is_assign_target(node): - head = results["head"] - if "".join([str(n) for n in head]).strip() == '__builtin__': - self.warning(node, bind_warning) - return - # Omit this: - # attr.replace(Name("__next__")) - elif "global" in results: - self.warning(node, bind_warning) - self.shadowed_next = True - - -### The following functions help test if node is part of an assignment -### target. - -def is_assign_target(node): - assign = find_assign(node) - if assign is None: - return False - - for child in assign.children: - if child.type == token.EQUAL: - return False - elif is_subtree(child, node): - return True - return False - -def find_assign(node): - if node.type == syms.expr_stmt: - return node - if node.type == syms.simple_stmt or node.parent is None: - return None - return find_assign(node.parent) - -def is_subtree(root, node): - if root == node: - return True - return any(is_subtree(c, node) for c in root.children) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_object.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_object.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_object.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_object.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -""" -Fixer that adds ``from builtins import object`` if there is a line -like this: - class Foo(object): -""" - -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -class FixObject(fixer_base.BaseFix): - - PATTERN = u"classdef< 'class' NAME '(' name='object' ')' colon=':' any >" - - def transform(self, node, results): - touch_import_top(u'builtins', 'object', node) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line: - - from past.builtins import str as oldstr - -at the top and wraps any unadorned string literals 'abc' or explicit byte-string -literals b'abc' in oldstr() calls so the code has the same behaviour on Py3 as -on Py2.6/2.7. -""" - -from __future__ import unicode_literals -import re -from lib2to3 import fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import syms -from libfuturize.fixer_util import (future_import, touch_import_top, - wrap_in_fn_call) - - -_literal_re = re.compile(r"[^uUrR]?[\'\"]") - - -class FixOldstrWrap(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "STRING" - - def transform(self, node, results): - if node.type == token.STRING: - touch_import_top(u'past.types', u'oldstr', node) - if _literal_re.match(node.value): - new = node.clone() - # Strip any leading space or comments: - # TODO: check: do we really want to do this? - new.prefix = u'' - new.value = u'b' + new.value - wrapped = wrap_in_fn_call("oldstr", [new], prefix=node.prefix) - return wrapped diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -""" -UNFINISHED - -Fixer for turning multiple lines like these: - - from __future__ import division - from __future__ import absolute_import - from __future__ import print_function - -into a single line like this: - - from __future__ import (absolute_import, division, print_function) - -This helps with testing of ``futurize``. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixOrderFutureImports(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - - run_order = 10 - - # def match(self, node): - # """ - # Match only once per file - # """ - # if hasattr(node, 'type') and node.type == syms.file_input: - # return True - # return False - - def transform(self, node, results): - # TODO # write me - pass - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -# Copyright 2006 Google, Inc. All Rights Reserved. -# Licensed to PSF under a Contributor Agreement. - -"""Fixer for print. - -Change: - "print" into "print()" - "print ..." into "print(...)" - "print(...)" not changed - "print ... ," into "print(..., end=' ')" - "print >>x, ..." into "print(..., file=x)" - -No changes are applied if print_function is imported from __future__ - -""" - -# Local imports -from lib2to3 import patcomp, pytree, fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import Name, Call, Comma, String -# from libmodernize import add_future - -parend_expr = patcomp.compile_pattern( - """atom< '(' [arith_expr|atom|power|term|STRING|NAME] ')' >""" - ) - - -class FixPrint(fixer_base.BaseFix): - - BM_compatible = True - - PATTERN = """ - simple_stmt< any* bare='print' any* > | print_stmt - """ - - def transform(self, node, results): - assert results - - bare_print = results.get("bare") - - if bare_print: - # Special-case print all by itself. - bare_print.replace(Call(Name(u"print"), [], - prefix=bare_print.prefix)) - # The "from __future__ import print_function"" declaration is added - # by the fix_print_with_import fixer, so we skip it here. - # add_future(node, u'print_function') - return - assert node.children[0] == Name(u"print") - args = node.children[1:] - if len(args) == 1 and parend_expr.match(args[0]): - # We don't want to keep sticking parens around an - # already-parenthesised expression. - return - - sep = end = file = None - if args and args[-1] == Comma(): - args = args[:-1] - end = " " - if args and args[0] == pytree.Leaf(token.RIGHTSHIFT, u">>"): - assert len(args) >= 2 - file = args[1].clone() - args = args[3:] # Strip a possible comma after the file expression - # Now synthesize a print(args, sep=..., end=..., file=...) node. - l_args = [arg.clone() for arg in args] - if l_args: - l_args[0].prefix = u"" - if sep is not None or end is not None or file is not None: - if sep is not None: - self.add_kwarg(l_args, u"sep", String(repr(sep))) - if end is not None: - self.add_kwarg(l_args, u"end", String(repr(end))) - if file is not None: - self.add_kwarg(l_args, u"file", file) - n_stmt = Call(Name(u"print"), l_args) - n_stmt.prefix = node.prefix - - # Note that there are corner cases where adding this future-import is - # incorrect, for example when the file also has a 'print ()' statement - # that was intended to print "()". - # add_future(node, u'print_function') - return n_stmt - - def add_kwarg(self, l_nodes, s_kwd, n_expr): - # XXX All this prefix-setting may lose comments (though rarely) - n_expr.prefix = u"" - n_argument = pytree.Node(self.syms.argument, - (Name(s_kwd), - pytree.Leaf(token.EQUAL, u"="), - n_expr)) - if l_nodes: - l_nodes.append(Comma()) - n_argument.prefix = u" " - l_nodes.append(n_argument) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -""" -For the ``future`` package. - -Turns any print statements into functions and adds this import line: - - from __future__ import print_function - -at the top to retain compatibility with Python 2.6+. -""" - -from libfuturize.fixes.fix_print import FixPrint -from libfuturize.fixer_util import future_import - -class FixPrintWithImport(FixPrint): - run_order = 7 - def transform(self, node, results): - # Add the __future__ import first. (Otherwise any shebang or encoding - # comment line attached as a prefix to the print statement will be - # copied twice and appear twice.) - future_import(u'print_function', node) - n_stmt = super(FixPrintWithImport, self).transform(node, results) - return n_stmt - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -"""Fixer for 'raise E, V' - -From Armin Ronacher's ``python-modernize``. - -raise -> raise -raise E -> raise E -raise E, V -> raise E(V) - -raise (((E, E'), E''), E'''), V -> raise E(V) - - -CAVEATS: -1) "raise E, V" will be incorrectly translated if V is an exception - instance. The correct Python 3 idiom is - - raise E from V - - but since we can't detect instance-hood by syntax alone and since - any client code would have to be changed as well, we don't automate - this. -""" -# Author: Collin Winter, Armin Ronacher - -# Local imports -from lib2to3 import pytree, fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import Name, Call, is_tuple - -class FixRaise(fixer_base.BaseFix): - - BM_compatible = True - PATTERN = """ - raise_stmt< 'raise' exc=any [',' val=any] > - """ - - def transform(self, node, results): - syms = self.syms - - exc = results["exc"].clone() - if exc.type == token.STRING: - msg = "Python 3 does not support string exceptions" - self.cannot_convert(node, msg) - return - - # Python 2 supports - # raise ((((E1, E2), E3), E4), E5), V - # as a synonym for - # raise E1, V - # Since Python 3 will not support this, we recurse down any tuple - # literals, always taking the first element. - if is_tuple(exc): - while is_tuple(exc): - # exc.children[1:-1] is the unparenthesized tuple - # exc.children[1].children[0] is the first element of the tuple - exc = exc.children[1].children[0].clone() - exc.prefix = u" " - - if "val" not in results: - # One-argument raise - new = pytree.Node(syms.raise_stmt, [Name(u"raise"), exc]) - new.prefix = node.prefix - return new - - val = results["val"].clone() - if is_tuple(val): - args = [c.clone() for c in val.children[1:-1]] - else: - val.prefix = u"" - args = [val] - - return pytree.Node(syms.raise_stmt, - [Name(u"raise"), Call(exc, args)], - prefix=node.prefix) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -""" -Fixer for removing any of these lines: - - from __future__ import with_statement - from __future__ import nested_scopes - from __future__ import generators - -The reason is that __future__ imports like these are required to be the first -line of code (after docstrings) on Python 2.6+, which can get in the way. - -These imports are always enabled in Python 2.6+, which is the minimum sane -version to target for Py2/3 compatibility. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import remove_future_import - -class FixRemoveOldFutureImports(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 1 - - def transform(self, node, results): - remove_future_import(u"with_statement", node) - remove_future_import(u"nested_scopes", node) - remove_future_import(u"generators", node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -"""Fixer that changes unicode to str and unichr to chr, but -- unlike the -lib2to3 fix_unicode.py fixer, does not change u"..." into "...". - -The reason is that Py3.3+ supports the u"..." string prefix, and, if -present, the prefix may provide useful information for disambiguating -between byte strings and unicode strings, which is often the hardest part -of the porting task. - -""" - -from lib2to3.pgen2 import token -from lib2to3 import fixer_base - -_mapping = {u"unichr" : u"chr", u"unicode" : u"str"} - -class FixUnicodeKeepU(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "'unicode' | 'unichr'" - - def transform(self, node, results): - if node.type == token.NAME: - new = node.clone() - new.value = _mapping[node.value] - return new - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -""" -Adds this import: - - from __future__ import unicode_literals - -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixUnicodeLiteralsImport(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - - run_order = 9 - - def transform(self, node, results): - future_import(u"unicode_literals", node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -"""Fix UserDict. - -Incomplete! - -TODO: base this on fix_urllib perhaps? -""" - - -# Local imports -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, attr_chain -from lib2to3.fixes.fix_imports import alternates, build_pattern, FixImports - -MAPPING = {'UserDict': 'collections', -} - -# def alternates(members): -# return "(" + "|".join(map(repr, members)) + ")" -# -# -# def build_pattern(mapping=MAPPING): -# mod_list = ' | '.join(["module_name='%s'" % key for key in mapping]) -# bare_names = alternates(mapping.keys()) -# -# yield """name_import=import_name< 'import' ((%s) | -# multiple_imports=dotted_as_names< any* (%s) any* >) > -# """ % (mod_list, mod_list) -# yield """import_from< 'from' (%s) 'import' ['('] -# ( any | import_as_name< any 'as' any > | -# import_as_names< any* >) [')'] > -# """ % mod_list -# yield """import_name< 'import' (dotted_as_name< (%s) 'as' any > | -# multiple_imports=dotted_as_names< -# any* dotted_as_name< (%s) 'as' any > any* >) > -# """ % (mod_list, mod_list) -# -# # Find usages of module members in code e.g. thread.foo(bar) -# yield "power< bare_with_attr=(%s) trailer<'.' any > any* >" % bare_names - - -# class FixUserDict(fixer_base.BaseFix): -class FixUserdict(FixImports): - - BM_compatible = True - keep_line_order = True - # This is overridden in fix_imports2. - mapping = MAPPING - - # We want to run this fixer late, so fix_import doesn't try to make stdlib - # renames into relative imports. - run_order = 6 - - def build_pattern(self): - return "|".join(build_pattern(self.mapping)) - - def compile_pattern(self): - # We override this, so MAPPING can be pragmatically altered and the - # changes will be reflected in PATTERN. - self.PATTERN = self.build_pattern() - super(FixImports, self).compile_pattern() - - # Don't match the node if it's within another match. - def match(self, node): - match = super(FixImports, self).match - results = match(node) - if results: - # Module usage could be in the trailer of an attribute lookup, so we - # might have nested matches when "bare_with_attr" is present. - if "bare_with_attr" not in results and \ - any(match(obj) for obj in attr_chain(node, "parent")): - return False - return results - return False - - def start_tree(self, tree, filename): - super(FixImports, self).start_tree(tree, filename) - self.replace = {} - - def transform(self, node, results): - import_mod = results.get("module_name") - if import_mod: - mod_name = import_mod.value - new_name = unicode(self.mapping[mod_name]) - import_mod.replace(Name(new_name, prefix=import_mod.prefix)) - if "name_import" in results: - # If it's not a "from x import x, y" or "import x as y" import, - # marked its usage to be replaced. - self.replace[mod_name] = new_name - if "multiple_imports" in results: - # This is a nasty hack to fix multiple imports on a line (e.g., - # "import StringIO, urlparse"). The problem is that I can't - # figure out an easy way to make a pattern recognize the keys of - # MAPPING randomly sprinkled in an import statement. - results = self.match(node) - if results: - self.transform(node, results) - else: - # Replace usage of the module. - bare_name = results["bare_with_attr"][0] - new_name = self.replace.get(bare_name.value) - if new_name: - bare_name.replace(Name(new_name, prefix=bare_name.prefix)) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -""" -For the ``future`` package. - -Turns any xrange calls into range calls and adds this import line: - - from builtins import range - -at the top. -""" - -from lib2to3.fixes.fix_xrange import FixXrange - -from libfuturize.fixer_util import touch_import_top - - -class FixXrangeWithImport(FixXrange): - def transform(self, node, results): - result = super(FixXrangeWithImport, self).transform(node, results) - touch_import_top('builtins', 'range', node) - return result diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/fixes/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/fixes/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -import sys -from lib2to3 import refactor - -# The following fixers are "safe": they convert Python 2 code to more -# modern Python 2 code. They should be uncontroversial to apply to most -# projects that are happy to drop support for Py2.5 and below. Applying -# them first will reduce the size of the patch set for the real porting. -lib2to3_fix_names_stage1 = set([ - 'lib2to3.fixes.fix_apply', - 'lib2to3.fixes.fix_except', - 'lib2to3.fixes.fix_exec', - 'lib2to3.fixes.fix_exitfunc', - 'lib2to3.fixes.fix_funcattrs', - 'lib2to3.fixes.fix_has_key', - 'lib2to3.fixes.fix_idioms', - # 'lib2to3.fixes.fix_import', # makes any implicit relative imports explicit. (Use with ``from __future__ import absolute_import) - 'lib2to3.fixes.fix_intern', - 'lib2to3.fixes.fix_isinstance', - 'lib2to3.fixes.fix_methodattrs', - 'lib2to3.fixes.fix_ne', - # 'lib2to3.fixes.fix_next', # would replace ``next`` method names - # with ``__next__``. - 'lib2to3.fixes.fix_numliterals', # turns 1L into 1, 0755 into 0o755 - 'lib2to3.fixes.fix_paren', - # 'lib2to3.fixes.fix_print', # see the libfuturize fixer that also - # adds ``from __future__ import print_function`` - # 'lib2to3.fixes.fix_raise', # uses incompatible with_traceback() method on exceptions - 'lib2to3.fixes.fix_reduce', # reduce is available in functools on Py2.6/Py2.7 - 'lib2to3.fixes.fix_renames', # sys.maxint -> sys.maxsize - # 'lib2to3.fixes.fix_set_literal', # this is unnecessary and breaks Py2.6 support - 'lib2to3.fixes.fix_repr', - 'lib2to3.fixes.fix_standarderror', - 'lib2to3.fixes.fix_sys_exc', - 'lib2to3.fixes.fix_throw', - 'lib2to3.fixes.fix_tuple_params', - 'lib2to3.fixes.fix_types', - 'lib2to3.fixes.fix_ws_comma', # can perhaps decrease readability: see issue #58 - 'lib2to3.fixes.fix_xreadlines', -]) - -# The following fixers add a dependency on the ``future`` package on order to -# support Python 2: -lib2to3_fix_names_stage2 = set([ - # 'lib2to3.fixes.fix_buffer', # perhaps not safe. Test this. - # 'lib2to3.fixes.fix_callable', # not needed in Py3.2+ - 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. and move to stage2 - # 'lib2to3.fixes.fix_execfile', # some problems: see issue #37. - # We use a custom fixer instead (see below) - # 'lib2to3.fixes.fix_future', # we don't want to remove __future__ imports - 'lib2to3.fixes.fix_getcwdu', - # 'lib2to3.fixes.fix_imports', # called by libfuturize.fixes.fix_future_standard_library - # 'lib2to3.fixes.fix_imports2', # we don't handle this yet (dbm) - 'lib2to3.fixes.fix_input', - 'lib2to3.fixes.fix_itertools', - 'lib2to3.fixes.fix_itertools_imports', - 'lib2to3.fixes.fix_filter', - 'lib2to3.fixes.fix_long', - 'lib2to3.fixes.fix_map', - # 'lib2to3.fixes.fix_metaclass', # causes SyntaxError in Py2! Use the one from ``six`` instead - 'lib2to3.fixes.fix_next', - 'lib2to3.fixes.fix_nonzero', # TODO: cause this to import ``object`` and/or add a decorator for mapping __bool__ to __nonzero__ - 'lib2to3.fixes.fix_operator', # we will need support for this by e.g. extending the Py2 operator module to provide those functions in Py3 - 'lib2to3.fixes.fix_raw_input', - # 'lib2to3.fixes.fix_unicode', # strips off the u'' prefix, which removes a potentially helpful source of information for disambiguating unicode/byte strings - # 'lib2to3.fixes.fix_urllib', # included in libfuturize.fix_future_standard_library_urllib - # 'lib2to3.fixes.fix_xrange', # custom one because of a bug with Py3.3's lib2to3 - 'lib2to3.fixes.fix_zip', -]) - -libfuturize_fix_names_stage1 = set([ - 'libfuturize.fixes.fix_absolute_import', - 'libfuturize.fixes.fix_next_call', # obj.next() -> next(obj). Unlike - # lib2to3.fixes.fix_next, doesn't change - # the ``next`` method to ``__next__``. - 'libfuturize.fixes.fix_print_with_import', - 'libfuturize.fixes.fix_raise', - # 'libfuturize.fixes.fix_order___future__imports', # TODO: consolidate to a single line to simplify testing -]) - -libfuturize_fix_names_stage2 = set([ - 'libfuturize.fixes.fix_basestring', - # 'libfuturize.fixes.fix_add__future__imports_except_unicode_literals', # just in case - 'libfuturize.fixes.fix_cmp', - 'libfuturize.fixes.fix_division_safe', - 'libfuturize.fixes.fix_execfile', - 'libfuturize.fixes.fix_future_builtins', - 'libfuturize.fixes.fix_future_standard_library', - 'libfuturize.fixes.fix_future_standard_library_urllib', - 'libfuturize.fixes.fix_metaclass', - 'libpasteurize.fixes.fix_newstyle', - 'libfuturize.fixes.fix_object', - # 'libfuturize.fixes.fix_order___future__imports', # TODO: consolidate to a single line to simplify testing - 'libfuturize.fixes.fix_unicode_keep_u', - # 'libfuturize.fixes.fix_unicode_literals_import', - 'libfuturize.fixes.fix_xrange_with_import', # custom one because of a bug with Py3.3's lib2to3 -]) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -# empty to make this a package diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/main.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/main.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libfuturize/main.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libfuturize/main.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,322 +0,0 @@ -""" -futurize: automatic conversion to clean 2/3 code using ``python-future`` -====================================================================== - -Like Armin Ronacher's modernize.py, ``futurize`` attempts to produce clean -standard Python 3 code that runs on both Py2 and Py3. - -One pass --------- - -Use it like this on Python 2 code: - - $ futurize --verbose mypython2script.py - -This will attempt to port the code to standard Py3 code that also -provides Py2 compatibility with the help of the right imports from -``future``. - -To write changes to the files, use the -w flag. - -Two stages ----------- - -The ``futurize`` script can also be called in two separate stages. First: - - $ futurize --stage1 mypython2script.py - -This produces more modern Python 2 code that is not yet compatible with Python -3. The tests should still run and the diff should be uncontroversial to apply to -most Python projects that are willing to drop support for Python 2.5 and lower. - -After this, the recommended approach is to explicitly mark all strings that must -be byte-strings with a b'' prefix and all text (unicode) strings with a u'' -prefix, and then invoke the second stage of Python 2 to 2/3 conversion with:: - - $ futurize --stage2 mypython2script.py - -Stage 2 adds a dependency on ``future``. It converts most remaining Python -2-specific code to Python 3 code and adds appropriate imports from ``future`` -to restore Py2 support. - -The command above leaves all unadorned string literals as native strings -(byte-strings on Py2, unicode strings on Py3). If instead you would like all -unadorned string literals to be promoted to unicode, you can also pass this -flag: - - $ futurize --stage2 --unicode-literals mypython2script.py - -This adds the declaration ``from __future__ import unicode_literals`` to the -top of each file, which implicitly declares all unadorned string literals to be -unicode strings (``unicode`` on Py2). - -All imports ------------ - -The --all-imports option forces adding all ``__future__`` imports, -``builtins`` imports, and standard library aliases, even if they don't -seem necessary for the current state of each module. (This can simplify -testing, and can reduce the need to think about Py2 compatibility when editing -the code further.) - -""" - -from __future__ import (absolute_import, print_function, unicode_literals) -import future.utils -from future import __version__ - -import sys -import logging -import optparse -import os - -from lib2to3.main import main, warn, StdoutRefactoringTool -from lib2to3 import refactor - -from libfuturize.fixes import (lib2to3_fix_names_stage1, - lib2to3_fix_names_stage2, - libfuturize_fix_names_stage1, - libfuturize_fix_names_stage2) - -fixer_pkg = 'libfuturize.fixes' - - -def main(args=None): - """Main program. - - Args: - fixer_pkg: the name of a package where the fixers are located. - args: optional; a list of command line arguments. If omitted, - sys.argv[1:] is used. - - Returns a suggested exit status (0, 1, 2). - """ - - # Set up option parser - parser = optparse.OptionParser(usage="futurize [options] file|dir ...") - parser.add_option("-V", "--version", action="store_true", - help="Report the version number of futurize") - parser.add_option("-a", "--all-imports", action="store_true", - help="Add all __future__ and future imports to each module") - parser.add_option("-1", "--stage1", action="store_true", - help="Modernize Python 2 code only; no compatibility with Python 3 (or dependency on ``future``)") - parser.add_option("-2", "--stage2", action="store_true", - help="Take modernized (stage1) code and add a dependency on ``future`` to provide Py3 compatibility.") - parser.add_option("-0", "--both-stages", action="store_true", - help="Apply both stages 1 and 2") - parser.add_option("-u", "--unicode-literals", action="store_true", - help="Add ``from __future__ import unicode_literals`` to implicitly convert all unadorned string literals '' into unicode strings") - parser.add_option("-f", "--fix", action="append", default=[], - help="Each FIX specifies a transformation; default: all.\nEither use '-f division -f metaclass' etc. or use the fully-qualified module name: '-f lib2to3.fixes.fix_types -f libfuturize.fixes.fix_unicode_keep_u'") - parser.add_option("-j", "--processes", action="store", default=1, - type="int", help="Run 2to3 concurrently") - parser.add_option("-x", "--nofix", action="append", default=[], - help="Prevent a fixer from being run.") - parser.add_option("-l", "--list-fixes", action="store_true", - help="List available transformations") - parser.add_option("-p", "--print-function", action="store_true", - help="Modify the grammar so that print() is a function") - parser.add_option("-v", "--verbose", action="store_true", - help="More verbose logging") - parser.add_option("--no-diffs", action="store_true", - help="Don't show diffs of the refactoring") - parser.add_option("-w", "--write", action="store_true", - help="Write back modified files") - parser.add_option("-n", "--nobackups", action="store_true", default=False, - help="Don't write backups for modified files.") - parser.add_option("-o", "--output-dir", action="store", type="str", - default="", help="Put output files in this directory " - "instead of overwriting the input files. Requires -n. " - "For Python >= 2.7 only.") - parser.add_option("-W", "--write-unchanged-files", action="store_true", - help="Also write files even if no changes were required" - " (useful with --output-dir); implies -w.") - parser.add_option("--add-suffix", action="store", type="str", default="", - help="Append this string to all output filenames." - " Requires -n if non-empty. For Python >= 2.7 only." - "ex: --add-suffix='3' will generate .py3 files.") - - # Parse command line arguments - flags = {} - refactor_stdin = False - options, args = parser.parse_args(args) - - if options.write_unchanged_files: - flags["write_unchanged_files"] = True - if not options.write: - warn("--write-unchanged-files/-W implies -w.") - options.write = True - # If we allowed these, the original files would be renamed to backup names - # but not replaced. - if options.output_dir and not options.nobackups: - parser.error("Can't use --output-dir/-o without -n.") - if options.add_suffix and not options.nobackups: - parser.error("Can't use --add-suffix without -n.") - - if not options.write and options.no_diffs: - warn("not writing files and not printing diffs; that's not very useful") - if not options.write and options.nobackups: - parser.error("Can't use -n without -w") - if "-" in args: - refactor_stdin = True - if options.write: - print("Can't write to stdin.", file=sys.stderr) - return 2 - # Is this ever necessary? - if options.print_function: - flags["print_function"] = True - - # Set up logging handler - level = logging.DEBUG if options.verbose else logging.INFO - logging.basicConfig(format='%(name)s: %(message)s', level=level) - logger = logging.getLogger('libfuturize.main') - - if options.stage1 or options.stage2: - assert options.both_stages is None - options.both_stages = False - else: - options.both_stages = True - - avail_fixes = set() - - if options.stage1 or options.both_stages: - avail_fixes.update(lib2to3_fix_names_stage1) - avail_fixes.update(libfuturize_fix_names_stage1) - if options.stage2 or options.both_stages: - avail_fixes.update(lib2to3_fix_names_stage2) - avail_fixes.update(libfuturize_fix_names_stage2) - - if options.unicode_literals: - avail_fixes.add('libfuturize.fixes.fix_unicode_literals_import') - - if options.version: - print(__version__) - return 0 - if options.list_fixes: - print("Available transformations for the -f/--fix option:") - # for fixname in sorted(refactor.get_all_fix_names(fixer_pkg)): - for fixname in sorted(avail_fixes): - print(fixname) - if not args: - return 0 - if not args: - print("At least one file or directory argument required.", - file=sys.stderr) - print("Use --help to show usage.", file=sys.stderr) - return 2 - - unwanted_fixes = set() - for fix in options.nofix: - if ".fix_" in fix: - unwanted_fixes.add(fix) - else: - # Infer the full module name for the fixer. - # First ensure that no names clash (e.g. - # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah): - found = [f for f in avail_fixes - if f.endswith('fix_{0}'.format(fix))] - if len(found) > 1: - print("Ambiguous fixer name. Choose a fully qualified " - "module name instead from these:\n" + - "\n".join(" " + myf for myf in found), - file=sys.stderr) - return 2 - elif len(found) == 0: - print("Unknown fixer. Use --list-fixes or -l for a list.", - file=sys.stderr) - return 2 - unwanted_fixes.add(found[0]) - - extra_fixes = set() - if options.all_imports: - if options.stage1: - prefix = 'libfuturize.fixes.' - extra_fixes.add(prefix + - 'fix_add__future__imports_except_unicode_literals') - else: - # In case the user hasn't run stage1 for some reason: - prefix = 'libpasteurize.fixes.' - extra_fixes.add(prefix + 'fix_add_all__future__imports') - extra_fixes.add(prefix + 'fix_add_future_standard_library_import') - extra_fixes.add(prefix + 'fix_add_all_future_builtins') - explicit = set() - if options.fix: - all_present = False - for fix in options.fix: - if fix == 'all': - all_present = True - else: - if ".fix_" in fix: - explicit.add(fix) - else: - # Infer the full module name for the fixer. - # First ensure that no names clash (e.g. - # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah): - found = [f for f in avail_fixes - if f.endswith('fix_{0}'.format(fix))] - if len(found) > 1: - print("Ambiguous fixer name. Choose a fully qualified " - "module name instead from these:\n" + - "\n".join(" " + myf for myf in found), - file=sys.stderr) - return 2 - elif len(found) == 0: - print("Unknown fixer. Use --list-fixes or -l for a list.", - file=sys.stderr) - return 2 - explicit.add(found[0]) - if len(explicit & unwanted_fixes) > 0: - print("Conflicting usage: the following fixers have been " - "simultaneously requested and disallowed:\n" + - "\n".join(" " + myf for myf in (explicit & unwanted_fixes)), - file=sys.stderr) - return 2 - requested = avail_fixes.union(explicit) if all_present else explicit - else: - requested = avail_fixes.union(explicit) - fixer_names = (requested | extra_fixes) - unwanted_fixes - - input_base_dir = os.path.commonprefix(args) - if (input_base_dir and not input_base_dir.endswith(os.sep) - and not os.path.isdir(input_base_dir)): - # One or more similar names were passed, their directory is the base. - # os.path.commonprefix() is ignorant of path elements, this corrects - # for that weird API. - input_base_dir = os.path.dirname(input_base_dir) - if options.output_dir: - input_base_dir = input_base_dir.rstrip(os.sep) - logger.info('Output in %r will mirror the input directory %r layout.', - options.output_dir, input_base_dir) - - # Initialize the refactoring tool - if future.utils.PY26: - extra_kwargs = {} - else: - extra_kwargs = { - 'append_suffix': options.add_suffix, - 'output_dir': options.output_dir, - 'input_base_dir': input_base_dir, - } - - rt = StdoutRefactoringTool( - sorted(fixer_names), flags, sorted(explicit), - options.nobackups, not options.no_diffs, - **extra_kwargs) - - # Refactor all files and directories passed as arguments - if not rt.errors: - if refactor_stdin: - rt.refactor_stdin() - else: - try: - rt.refactor(args, options.write, None, - options.processes) - except refactor.MultiprocessingUnsupported: - assert options.processes > 1 - print("Sorry, -j isn't " \ - "supported on this platform.", file=sys.stderr) - return 1 - rt.summarize() - - # Return error status (0 if rt.errors is zero) - return int(bool(rt.errors)) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -u""" -Base classes for features that are backwards-incompatible. - -Usage: -features = Features() -features.add(Feature("py3k_feature", "power< 'py3k' any* >", "2.7")) -PATTERN = features.PATTERN -""" - -pattern_unformatted = u"%s=%s" # name=pattern, for dict lookups -message_unformatted = u""" -%s is only supported in Python %s and above.""" - -class Feature(object): - u""" - A feature has a name, a pattern, and a minimum version of Python 2.x - required to use the feature (or 3.x if there is no backwards-compatible - version of 2.x) - """ - def __init__(self, name, PATTERN, version): - self.name = name - self._pattern = PATTERN - self.version = version - - def message_text(self): - u""" - Format the above text with the name and minimum version required. - """ - return message_unformatted % (self.name, self.version) - -class Features(set): - u""" - A set of features that generates a pattern for the features it contains. - This set will act like a mapping in that we map names to patterns. - """ - mapping = {} - - def update_mapping(self): - u""" - Called every time we care about the mapping of names to features. - """ - self.mapping = dict([(f.name, f) for f in iter(self)]) - - @property - def PATTERN(self): - u""" - Uses the mapping of names to features to return a PATTERN suitable - for using the lib2to3 patcomp. - """ - self.update_mapping() - return u" |\n".join([pattern_unformatted % (f.name, f._pattern) for f in iter(self)]) - - def __getitem__(self, key): - u""" - Implement a simple mapping to get patterns from names. - """ - return self.mapping[key] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line:: - - from builtins import (ascii, bytes, chr, dict, filter, hex, input, - int, list, map, next, object, oct, open, pow, - range, round, str, super, zip) - -to a module, irrespective of whether each definition is used. - -Adds these imports after any other imports (in an initial block of them). -""" - -from __future__ import unicode_literals - -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -class FixAddAllFutureBuiltins(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 1 - - def transform(self, node, results): - # import_str = """(ascii, bytes, chr, dict, filter, hex, input, - # int, list, map, next, object, oct, open, pow, - # range, round, str, super, zip)""" - touch_import_top(u'builtins', '*', node) - - # builtins = """ascii bytes chr dict filter hex input - # int list map next object oct open pow - # range round str super zip""" - # for builtin in sorted(builtins.split(), reverse=True): - # touch_import_top(u'builtins', builtin, node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -""" -Fixer for adding: - - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - from __future__ import unicode_literals - -This is done when converting from Py3 to both Py3/Py2. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixAddAllFutureImports(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 1 - - def transform(self, node, results): - future_import(u"unicode_literals", node) - future_import(u"print_function", node) - future_import(u"division", node) - future_import(u"absolute_import", node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line: - - from future import standard_library - -after any __future__ imports but before any other imports. Doesn't actually -change the imports to Py3 style. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import touch_import_top - -class FixAddFutureStandardLibraryImport(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 8 - - def transform(self, node, results): - # TODO: add a blank line between any __future__ imports and this? - touch_import_top(u'future', u'standard_library', node) - # TODO: also add standard_library.install_hooks() diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -u""" -Fixer to remove function annotations -""" - -from lib2to3 import fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import syms - -warning_text = u"Removing function annotations completely." - -def param_without_annotations(node): - return node.children[0] - -class FixAnnotations(fixer_base.BaseFix): - - warned = False - - def warn_once(self, node, reason): - if not self.warned: - self.warned = True - self.warning(node, reason=reason) - - PATTERN = u""" - funcdef< 'def' any parameters< '(' [params=any] ')' > ['->' ret=any] ':' any* > - """ - - def transform(self, node, results): - u""" - This just strips annotations from the funcdef completely. - """ - params = results.get(u"params") - ret = results.get(u"ret") - if ret is not None: - assert ret.prev_sibling.type == token.RARROW, u"Invalid return annotation" - self.warn_once(node, reason=warning_text) - ret.prev_sibling.remove() - ret.remove() - if params is None: return - if params.type == syms.typedargslist: - # more than one param in a typedargslist - for param in params.children: - if param.type == syms.tname: - self.warn_once(node, reason=warning_text) - param.replace(param_without_annotations(param)) - elif params.type == syms.tname: - # one param - self.warn_once(node, reason=warning_text) - params.replace(param_without_annotations(params)) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -u""" -Fixer for division: from __future__ import division if needed -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import token, future_import - -def match_division(node): - u""" - __future__.division redefines the meaning of a single slash for division, - so we match that and only that. - """ - slash = token.SLASH - return node.type == slash and not node.next_sibling.type == slash and \ - not node.prev_sibling.type == slash - -class FixDivision(fixer_base.BaseFix): - run_order = 4 # this seems to be ignored? - - def match(self, node): - u""" - Since the tree needs to be fixed once and only once if and only if it - matches, then we can start discarding matches after we make the first. - """ - return match_division(node) - - def transform(self, node, results): - future_import(u"division", node) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -u""" -Warn about features that are not present in Python 2.5, giving a message that -points to the earliest version of Python 2.x (or 3.x, if none) that supports it -""" - -from .feature_base import Feature, Features -from lib2to3 import fixer_base - -FEATURES = [ - #(FeatureName, - # FeaturePattern, - # FeatureMinVersion, - #), - (u"memoryview", - u"power < 'memoryview' trailer < '(' any* ')' > any* >", - u"2.7", - ), - (u"numbers", - u"""import_from< 'from' 'numbers' 'import' any* > | - import_name< 'import' ('numbers' dotted_as_names< any* 'numbers' any* >) >""", - u"2.6", - ), - (u"abc", - u"""import_name< 'import' ('abc' dotted_as_names< any* 'abc' any* >) > | - import_from< 'from' 'abc' 'import' any* >""", - u"2.6", - ), - (u"io", - u"""import_name< 'import' ('io' dotted_as_names< any* 'io' any* >) > | - import_from< 'from' 'io' 'import' any* >""", - u"2.6", - ), - (u"bin", - u"power< 'bin' trailer< '(' any* ')' > any* >", - u"2.6", - ), - (u"formatting", - u"power< any trailer< '.' 'format' > trailer< '(' any* ')' > >", - u"2.6", - ), - (u"nonlocal", - u"global_stmt< 'nonlocal' any* >", - u"3.0", - ), - (u"with_traceback", - u"trailer< '.' 'with_traceback' >", - u"3.0", - ), -] - -class FixFeatures(fixer_base.BaseFix): - - run_order = 9 # Wait until all other fixers have run to check for these - - # To avoid spamming, we only want to warn for each feature once. - features_warned = set() - - # Build features from the list above - features = Features([Feature(name, pattern, version) for \ - name, pattern, version in FEATURES]) - - PATTERN = features.PATTERN - - def match(self, node): - to_ret = super(FixFeatures, self).match(node) - # We want the mapping only to tell us the node's specific information. - try: - del to_ret[u'node'] - except Exception: - # We want it to delete the 'node' from the results - # if it's there, so we don't care if it fails for normal reasons. - pass - return to_ret - - def transform(self, node, results): - for feature_name in results: - if feature_name in self.features_warned: - continue - else: - curr_feature = self.features[feature_name] - if curr_feature.version >= u"3": - fail = self.cannot_convert - else: - fail = self.warning - fail(node, reason=curr_feature.message_text()) - self.features_warned.add(feature_name) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -u""" -Fixer for getfullargspec -> getargspec -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name - -warn_msg = u"some of the values returned by getfullargspec are not valid in Python 2 and have no equivalent." - -class FixFullargspec(fixer_base.BaseFix): - - PATTERN = u"'getfullargspec'" - - def transform(self, node, results): - self.warning(node, warn_msg) - return Name(u"getargspec", prefix=node.prefix) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -""" -Adds this import line: - - from builtins import XYZ - -for each of the functions XYZ that is used in the module. -""" - -from __future__ import unicode_literals - -from lib2to3 import fixer_base -from lib2to3.pygram import python_symbols as syms -from lib2to3.fixer_util import Name, Call, in_special_context - -from libfuturize.fixer_util import touch_import_top - -# All builtins are: -# from future.builtins.iterators import (filter, map, zip) -# from future.builtins.misc import (ascii, chr, hex, input, isinstance, oct, open, round, super) -# from future.types import (bytes, dict, int, range, str) -# We don't need isinstance any more. - -replaced_builtins = '''filter map zip - ascii chr hex input next oct open round super - bytes dict int range str'''.split() - -expression = '|'.join(["name='{0}'".format(name) for name in replaced_builtins]) - - -class FixFutureBuiltins(fixer_base.BaseFix): - BM_compatible = True - run_order = 9 - - # Currently we only match uses as a function. This doesn't match e.g.: - # if isinstance(s, str): - # ... - PATTERN = """ - power< - ({0}) trailer< '(' args=[any] ')' > - rest=any* > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'builtins', name.value, node) - # name.replace(Name(u"input", prefix=name.prefix)) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -u""" -Fixer for os.getcwd() -> os.getcwdu(). -Also warns about "from os import getcwd", suggesting the above form. -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name - -class FixGetcwd(fixer_base.BaseFix): - - PATTERN = u""" - power< 'os' trailer< dot='.' name='getcwd' > any* > - | - import_from< 'from' 'os' 'import' bad='getcwd' > - """ - - def transform(self, node, results): - if u"name" in results: - name = results[u"name"] - name.replace(Name(u"getcwdu", prefix=name.prefix)) - elif u"bad" in results: - # Can't convert to getcwdu and then expect to catch every use. - self.cannot_convert(node, u"import os, use os.getcwd() instead.") - return - else: - raise ValueError(u"For some reason, the pattern matcher failed.") diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -u""" -Fixer for complicated imports -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, String, FromImport, Newline, Comma -from libfuturize.fixer_util import touch_import_top - - -TK_BASE_NAMES = (u'ACTIVE', u'ALL', u'ANCHOR', u'ARC',u'BASELINE', u'BEVEL', u'BOTH', - u'BOTTOM', u'BROWSE', u'BUTT', u'CASCADE', u'CENTER', u'CHAR', - u'CHECKBUTTON', u'CHORD', u'COMMAND', u'CURRENT', u'DISABLED', - u'DOTBOX', u'E', u'END', u'EW', u'EXCEPTION', u'EXTENDED', u'FALSE', - u'FIRST', u'FLAT', u'GROOVE', u'HIDDEN', u'HORIZONTAL', u'INSERT', - u'INSIDE', u'LAST', u'LEFT', u'MITER', u'MOVETO', u'MULTIPLE', u'N', - u'NE', u'NO', u'NONE', u'NORMAL', u'NS', u'NSEW', u'NUMERIC', u'NW', - u'OFF', u'ON', u'OUTSIDE', u'PAGES', u'PIESLICE', u'PROJECTING', - u'RADIOBUTTON', u'RAISED', u'READABLE', u'RIDGE', u'RIGHT', - u'ROUND', u'S', u'SCROLL', u'SE', u'SEL', u'SEL_FIRST', u'SEL_LAST', - u'SEPARATOR', u'SINGLE', u'SOLID', u'SUNKEN', u'SW', u'StringTypes', - u'TOP', u'TRUE', u'TclVersion', u'TkVersion', u'UNDERLINE', - u'UNITS', u'VERTICAL', u'W', u'WORD', u'WRITABLE', u'X', u'Y', u'YES', - u'wantobjects') - -PY2MODULES = { - u'urllib2' : ( - u'AbstractBasicAuthHandler', u'AbstractDigestAuthHandler', - u'AbstractHTTPHandler', u'BaseHandler', u'CacheFTPHandler', - u'FTPHandler', u'FileHandler', u'HTTPBasicAuthHandler', - u'HTTPCookieProcessor', u'HTTPDefaultErrorHandler', - u'HTTPDigestAuthHandler', u'HTTPError', u'HTTPErrorProcessor', - u'HTTPHandler', u'HTTPPasswordMgr', - u'HTTPPasswordMgrWithDefaultRealm', u'HTTPRedirectHandler', - u'HTTPSHandler', u'OpenerDirector', u'ProxyBasicAuthHandler', - u'ProxyDigestAuthHandler', u'ProxyHandler', u'Request', - u'StringIO', u'URLError', u'UnknownHandler', u'addinfourl', - u'build_opener', u'install_opener', u'parse_http_list', - u'parse_keqv_list', u'randombytes', u'request_host', u'urlopen'), - u'urllib' : ( - u'ContentTooShortError', u'FancyURLopener',u'URLopener', - u'basejoin', u'ftperrors', u'getproxies', - u'getproxies_environment', u'localhost', u'pathname2url', - u'quote', u'quote_plus', u'splitattr', u'splithost', - u'splitnport', u'splitpasswd', u'splitport', u'splitquery', - u'splittag', u'splittype', u'splituser', u'splitvalue', - u'thishost', u'unquote', u'unquote_plus', u'unwrap', - u'url2pathname', u'urlcleanup', u'urlencode', u'urlopen', - u'urlretrieve',), - u'urlparse' : ( - u'parse_qs', u'parse_qsl', u'urldefrag', u'urljoin', - u'urlparse', u'urlsplit', u'urlunparse', u'urlunsplit'), - u'dbm' : ( - u'ndbm', u'gnu', u'dumb'), - u'anydbm' : ( - u'error', u'open'), - u'whichdb' : ( - u'whichdb',), - u'BaseHTTPServer' : ( - u'BaseHTTPRequestHandler', u'HTTPServer'), - u'CGIHTTPServer' : ( - u'CGIHTTPRequestHandler',), - u'SimpleHTTPServer' : ( - u'SimpleHTTPRequestHandler',), - u'FileDialog' : TK_BASE_NAMES + ( - u'FileDialog', u'LoadFileDialog', u'SaveFileDialog', - u'dialogstates', u'test'), - u'tkFileDialog' : ( - u'Directory', u'Open', u'SaveAs', u'_Dialog', u'askdirectory', - u'askopenfile', u'askopenfilename', u'askopenfilenames', - u'askopenfiles', u'asksaveasfile', u'asksaveasfilename'), - u'SimpleDialog' : TK_BASE_NAMES + ( - u'SimpleDialog',), - u'tkSimpleDialog' : TK_BASE_NAMES + ( - u'askfloat', u'askinteger', u'askstring', u'Dialog'), - u'SimpleXMLRPCServer' : ( - u'CGIXMLRPCRequestHandler', u'SimpleXMLRPCDispatcher', - u'SimpleXMLRPCRequestHandler', u'SimpleXMLRPCServer', - u'list_public_methods', u'remove_duplicates', - u'resolve_dotted_attribute'), - u'DocXMLRPCServer' : ( - u'DocCGIXMLRPCRequestHandler', u'DocXMLRPCRequestHandler', - u'DocXMLRPCServer', u'ServerHTMLDoc',u'XMLRPCDocGenerator'), - } - -MAPPING = { u'urllib.request' : - (u'urllib2', u'urllib'), - u'urllib.error' : - (u'urllib2', u'urllib'), - u'urllib.parse' : - (u'urllib2', u'urllib', u'urlparse'), - u'dbm.__init__' : - (u'anydbm', u'whichdb'), - u'http.server' : - (u'CGIHTTPServer', u'SimpleHTTPServer', u'BaseHTTPServer'), - u'tkinter.filedialog' : - (u'tkFileDialog', u'FileDialog'), - u'tkinter.simpledialog' : - (u'tkSimpleDialog', u'SimpleDialog'), - u'xmlrpc.server' : - (u'DocXMLRPCServer', u'SimpleXMLRPCServer'), - } - -# helps match 'http', as in 'from http.server import ...' -simple_name = u"name='%s'" -# helps match 'server', as in 'from http.server import ...' -simple_attr = u"attr='%s'" -# helps match 'HTTPServer', as in 'from http.server import HTTPServer' -simple_using = u"using='%s'" -# helps match 'urllib.request', as in 'import urllib.request' -dotted_name = u"dotted_name=dotted_name< %s '.' %s >" -# helps match 'http.server', as in 'http.server.HTTPServer(...)' -power_twoname = u"pow=power< %s trailer< '.' %s > trailer< '.' using=any > any* >" -# helps match 'dbm.whichdb', as in 'dbm.whichdb(...)' -power_onename = u"pow=power< %s trailer< '.' using=any > any* >" -# helps match 'from http.server import HTTPServer' -# also helps match 'from http.server import HTTPServer, SimpleHTTPRequestHandler' -# also helps match 'from http.server import *' -from_import = u"from_import=import_from< 'from' %s 'import' (import_as_name< using=any 'as' renamed=any> | in_list=import_as_names< using=any* > | using='*' | using=NAME) >" -# helps match 'import urllib.request' -name_import = u"name_import=import_name< 'import' (%s | in_list=dotted_as_names< imp_list=any* >) >" - -############# -# WON'T FIX # -############# - -# helps match 'import urllib.request as name' -name_import_rename = u"name_import_rename=dotted_as_name< %s 'as' renamed=any >" -# helps match 'from http import server' -from_import_rename = u"from_import_rename=import_from< 'from' %s 'import' (%s | import_as_name< %s 'as' renamed=any > | in_list=import_as_names< any* (%s | import_as_name< %s 'as' renamed=any >) any* >) >" - - -def all_modules_subpattern(): - u""" - Builds a pattern for all toplevel names - (urllib, http, etc) - """ - names_dot_attrs = [mod.split(u".") for mod in MAPPING] - ret = u"( " + u" | ".join([dotted_name % (simple_name % (mod[0]), - simple_attr % (mod[1])) for mod in names_dot_attrs]) - ret += u" | " - ret += u" | ".join([simple_name % (mod[0]) for mod in names_dot_attrs if mod[1] == u"__init__"]) + u" )" - return ret - - -def build_import_pattern(mapping1, mapping2): - u""" - mapping1: A dict mapping py3k modules to all possible py2k replacements - mapping2: A dict mapping py2k modules to the things they do - This builds a HUGE pattern to match all ways that things can be imported - """ - # py3k: urllib.request, py2k: ('urllib2', 'urllib') - yield from_import % (all_modules_subpattern()) - for py3k, py2k in mapping1.items(): - name, attr = py3k.split(u'.') - s_name = simple_name % (name) - s_attr = simple_attr % (attr) - d_name = dotted_name % (s_name, s_attr) - yield name_import % (d_name) - yield power_twoname % (s_name, s_attr) - if attr == u'__init__': - yield name_import % (s_name) - yield power_onename % (s_name) - yield name_import_rename % (d_name) - yield from_import_rename % (s_name, s_attr, s_attr, s_attr, s_attr) - - -class FixImports2(fixer_base.BaseFix): - - run_order = 4 - - PATTERN = u" | \n".join(build_import_pattern(MAPPING, PY2MODULES)) - - def transform(self, node, results): - touch_import_top(u'future', u'standard_library', node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -u""" -Fixer for standard library imports renamed in Python 3 -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, is_probably_builtin, Newline, does_tree_import -from lib2to3.pygram import python_symbols as syms -from lib2to3.pgen2 import token -from lib2to3.pytree import Node, Leaf - -from libfuturize.fixer_util import touch_import_top -# from ..fixer_util import NameImport - -# used in simple_mapping_to_pattern() -MAPPING = {u"reprlib": u"repr", - u"winreg": u"_winreg", - u"configparser": u"ConfigParser", - u"copyreg": u"copy_reg", - u"queue": u"Queue", - u"socketserver": u"SocketServer", - u"_markupbase": u"markupbase", - u"test.support": u"test.test_support", - u"dbm.bsd": u"dbhash", - u"dbm.ndbm": u"dbm", - u"dbm.dumb": u"dumbdbm", - u"dbm.gnu": u"gdbm", - u"html.parser": u"HTMLParser", - u"html.entities": u"htmlentitydefs", - u"http.client": u"httplib", - u"http.cookies": u"Cookie", - u"http.cookiejar": u"cookielib", -# "tkinter": "Tkinter", - u"tkinter.dialog": u"Dialog", - u"tkinter._fix": u"FixTk", - u"tkinter.scrolledtext": u"ScrolledText", - u"tkinter.tix": u"Tix", - u"tkinter.constants": u"Tkconstants", - u"tkinter.dnd": u"Tkdnd", - u"tkinter.__init__": u"Tkinter", - u"tkinter.colorchooser": u"tkColorChooser", - u"tkinter.commondialog": u"tkCommonDialog", - u"tkinter.font": u"tkFont", - u"tkinter.ttk": u"ttk", - u"tkinter.messagebox": u"tkMessageBox", - u"tkinter.turtle": u"turtle", - u"urllib.robotparser": u"robotparser", - u"xmlrpc.client": u"xmlrpclib", - u"builtins": u"__builtin__", -} - -# generic strings to help build patterns -# these variables mean (with http.client.HTTPConnection as an example): -# name = http -# attr = client -# used = HTTPConnection -# fmt_name is a formatted subpattern (simple_name_match or dotted_name_match) - -# helps match 'queue', as in 'from queue import ...' -simple_name_match = u"name='%s'" -# helps match 'client', to be used if client has been imported from http -subname_match = u"attr='%s'" -# helps match 'http.client', as in 'import urllib.request' -dotted_name_match = u"dotted_name=dotted_name< %s '.' %s >" -# helps match 'queue', as in 'queue.Queue(...)' -power_onename_match = u"%s" -# helps match 'http.client', as in 'http.client.HTTPConnection(...)' -power_twoname_match = u"power< %s trailer< '.' %s > any* >" -# helps match 'client.HTTPConnection', if 'client' has been imported from http -power_subname_match = u"power< %s any* >" -# helps match 'from http.client import HTTPConnection' -from_import_match = u"from_import=import_from< 'from' %s 'import' imported=any >" -# helps match 'from http import client' -from_import_submod_match = u"from_import_submod=import_from< 'from' %s 'import' (%s | import_as_name< %s 'as' renamed=any > | import_as_names< any* (%s | import_as_name< %s 'as' renamed=any >) any* > ) >" -# helps match 'import urllib.request' -name_import_match = u"name_import=import_name< 'import' %s > | name_import=import_name< 'import' dotted_as_name< %s 'as' renamed=any > >" -# helps match 'import http.client, winreg' -multiple_name_import_match = u"name_import=import_name< 'import' dotted_as_names< names=any* > >" - -def all_patterns(name): - u""" - Accepts a string and returns a pattern of possible patterns involving that name - Called by simple_mapping_to_pattern for each name in the mapping it receives. - """ - - # i_ denotes an import-like node - # u_ denotes a node that appears to be a usage of the name - if u'.' in name: - name, attr = name.split(u'.', 1) - simple_name = simple_name_match % (name) - simple_attr = subname_match % (attr) - dotted_name = dotted_name_match % (simple_name, simple_attr) - i_from = from_import_match % (dotted_name) - i_from_submod = from_import_submod_match % (simple_name, simple_attr, simple_attr, simple_attr, simple_attr) - i_name = name_import_match % (dotted_name, dotted_name) - u_name = power_twoname_match % (simple_name, simple_attr) - u_subname = power_subname_match % (simple_attr) - return u' | \n'.join((i_name, i_from, i_from_submod, u_name, u_subname)) - else: - simple_name = simple_name_match % (name) - i_name = name_import_match % (simple_name, simple_name) - i_from = from_import_match % (simple_name) - u_name = power_onename_match % (simple_name) - return u' | \n'.join((i_name, i_from, u_name)) - - -class FixImports(fixer_base.BaseFix): - - PATTERN = u' | \n'.join([all_patterns(name) for name in MAPPING]) - PATTERN = u' | \n'.join((PATTERN, multiple_name_import_match)) - - def transform(self, node, results): - touch_import_top(u'future', u'standard_library', node) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -u""" -Fixer for Python 3 function parameter syntax -This fixer is rather sensitive to incorrect py3k syntax. -""" - -# Note: "relevant" parameters are parameters following the first STAR in the list. - -from lib2to3 import fixer_base -from lib2to3.fixer_util import token, String, Newline, Comma, Name -from libfuturize.fixer_util import indentation, suitify, DoubleStar - -_assign_template = u"%(name)s = %(kwargs)s['%(name)s']; del %(kwargs)s['%(name)s']" -_if_template = u"if '%(name)s' in %(kwargs)s: %(assign)s" -_else_template = u"else: %(name)s = %(default)s" -_kwargs_default_name = u"_3to2kwargs" - -def gen_params(raw_params): - u""" - Generator that yields tuples of (name, default_value) for each parameter in the list - If no default is given, then it is default_value is None (not Leaf(token.NAME, 'None')) - """ - assert raw_params[0].type == token.STAR and len(raw_params) > 2 - curr_idx = 2 # the first place a keyword-only parameter name can be is index 2 - max_idx = len(raw_params) - while curr_idx < max_idx: - curr_item = raw_params[curr_idx] - prev_item = curr_item.prev_sibling - if curr_item.type != token.NAME: - curr_idx += 1 - continue - if prev_item is not None and prev_item.type == token.DOUBLESTAR: - break - name = curr_item.value - nxt = curr_item.next_sibling - if nxt is not None and nxt.type == token.EQUAL: - default_value = nxt.next_sibling - curr_idx += 2 - else: - default_value = None - yield (name, default_value) - curr_idx += 1 - -def remove_params(raw_params, kwargs_default=_kwargs_default_name): - u""" - Removes all keyword-only args from the params list and a bare star, if any. - Does not add the kwargs dict if needed. - Returns True if more action is needed, False if not - (more action is needed if no kwargs dict exists) - """ - assert raw_params[0].type == token.STAR - if raw_params[1].type == token.COMMA: - raw_params[0].remove() - raw_params[1].remove() - kw_params = raw_params[2:] - else: - kw_params = raw_params[3:] - for param in kw_params: - if param.type != token.DOUBLESTAR: - param.remove() - else: - return False - else: - return True - -def needs_fixing(raw_params, kwargs_default=_kwargs_default_name): - u""" - Returns string with the name of the kwargs dict if the params after the first star need fixing - Otherwise returns empty string - """ - found_kwargs = False - needs_fix = False - - for t in raw_params[2:]: - if t.type == token.COMMA: - # Commas are irrelevant at this stage. - continue - elif t.type == token.NAME and not found_kwargs: - # Keyword-only argument: definitely need to fix. - needs_fix = True - elif t.type == token.NAME and found_kwargs: - # Return 'foobar' of **foobar, if needed. - return t.value if needs_fix else u'' - elif t.type == token.DOUBLESTAR: - # Found either '*' from **foobar. - found_kwargs = True - else: - # Never found **foobar. Return a synthetic name, if needed. - return kwargs_default if needs_fix else u'' - -class FixKwargs(fixer_base.BaseFix): - - run_order = 7 # Run after function annotations are removed - - PATTERN = u"funcdef< 'def' NAME parameters< '(' arglist=typedargslist< params=any* > ')' > ':' suite=any >" - - def transform(self, node, results): - params_rawlist = results[u"params"] - for i, item in enumerate(params_rawlist): - if item.type == token.STAR: - params_rawlist = params_rawlist[i:] - break - else: - return - # params is guaranteed to be a list starting with *. - # if fixing is needed, there will be at least 3 items in this list: - # [STAR, COMMA, NAME] is the minimum that we need to worry about. - new_kwargs = needs_fixing(params_rawlist) - # new_kwargs is the name of the kwargs dictionary. - if not new_kwargs: - return - suitify(node) - - # At this point, params_rawlist is guaranteed to be a list - # beginning with a star that includes at least one keyword-only param - # e.g., [STAR, NAME, COMMA, NAME, COMMA, DOUBLESTAR, NAME] or - # [STAR, COMMA, NAME], or [STAR, COMMA, NAME, COMMA, DOUBLESTAR, NAME] - - # Anatomy of a funcdef: ['def', 'name', parameters, ':', suite] - # Anatomy of that suite: [NEWLINE, INDENT, first_stmt, all_other_stmts] - # We need to insert our new stuff before the first_stmt and change the - # first_stmt's prefix. - - suite = node.children[4] - first_stmt = suite.children[2] - ident = indentation(first_stmt) - - for name, default_value in gen_params(params_rawlist): - if default_value is None: - suite.insert_child(2, Newline()) - suite.insert_child(2, String(_assign_template %{u'name':name, u'kwargs':new_kwargs}, prefix=ident)) - else: - suite.insert_child(2, Newline()) - suite.insert_child(2, String(_else_template %{u'name':name, u'default':default_value}, prefix=ident)) - suite.insert_child(2, Newline()) - suite.insert_child(2, String(_if_template %{u'assign':_assign_template %{u'name':name, u'kwargs':new_kwargs}, u'name':name, u'kwargs':new_kwargs}, prefix=ident)) - first_stmt.prefix = ident - suite.children[2].prefix = u"" - - # Now, we need to fix up the list of params. - - must_add_kwargs = remove_params(params_rawlist) - if must_add_kwargs: - arglist = results[u'arglist'] - if len(arglist.children) > 0 and arglist.children[-1].type != token.COMMA: - arglist.append_child(Comma()) - arglist.append_child(DoubleStar(prefix=u" ")) - arglist.append_child(Name(new_kwargs)) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -u""" -Fixer for memoryview(s) -> buffer(s). -Explicit because some memoryview methods are invalid on buffer objects. -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name - - -class FixMemoryview(fixer_base.BaseFix): - - explicit = True # User must specify that they want this. - - PATTERN = u""" - power< name='memoryview' trailer< '(' [any] ')' > - rest=any* > - """ - - def transform(self, node, results): - name = results[u"name"] - name.replace(Name(u"buffer", prefix=name.prefix)) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -u""" -Fixer for (metaclass=X) -> __metaclass__ = X -Some semantics (see PEP 3115) may be altered in the translation.""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, syms, Node, Leaf, Newline, find_root -from lib2to3.pygram import token -from libfuturize.fixer_util import indentation, suitify -# from ..fixer_util import Name, syms, Node, Leaf, Newline, find_root, indentation, suitify - -def has_metaclass(parent): - results = None - for node in parent.children: - kids = node.children - if node.type == syms.argument: - if kids[0] == Leaf(token.NAME, u"metaclass") and \ - kids[1] == Leaf(token.EQUAL, u"=") and \ - kids[2]: - #Hack to avoid "class X(=):" with this case. - results = [node] + kids - break - elif node.type == syms.arglist: - # Argument list... loop through it looking for: - # Node(*, [*, Leaf(token.NAME, u"metaclass"), Leaf(token.EQUAL, u"="), Leaf(*, *)] - for child in node.children: - if results: break - if child.type == token.COMMA: - #Store the last comma, which precedes the metaclass - comma = child - elif type(child) == Node: - meta = equal = name = None - for arg in child.children: - if arg == Leaf(token.NAME, u"metaclass"): - #We have the (metaclass) part - meta = arg - elif meta and arg == Leaf(token.EQUAL, u"="): - #We have the (metaclass=) part - equal = arg - elif meta and equal: - #Here we go, we have (metaclass=X) - name = arg - results = (comma, meta, equal, name) - break - return results - - -class FixMetaclass(fixer_base.BaseFix): - - PATTERN = u""" - classdef - """ - - def transform(self, node, results): - meta_results = has_metaclass(node) - if not meta_results: return - for meta in meta_results: - meta.remove() - target = Leaf(token.NAME, u"__metaclass__") - equal = Leaf(token.EQUAL, u"=", prefix=u" ") - # meta is the last item in what was returned by has_metaclass(): name - name = meta - name.prefix = u" " - stmt_node = Node(syms.atom, [target, equal, name]) - - suitify(node) - for item in node.children: - if item.type == syms.suite: - for stmt in item.children: - if stmt.type == token.INDENT: - # Insert, in reverse order, the statement, a newline, - # and an indent right after the first indented line - loc = item.children.index(stmt) + 1 - # Keep consistent indentation form - ident = Leaf(token.INDENT, stmt.value) - item.insert_child(loc, ident) - item.insert_child(loc, Newline()) - item.insert_child(loc, stmt_node) - break diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -u""" -Fixer for "class Foo: ..." -> "class Foo(object): ..." -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import LParen, RParen, Name - -from libfuturize.fixer_util import touch_import_top - - -def insert_object(node, idx): - node.insert_child(idx, RParen()) - node.insert_child(idx, Name(u"object")) - node.insert_child(idx, LParen()) - -class FixNewstyle(fixer_base.BaseFix): - - # Match: - # class Blah: - # and: - # class Blah(): - - PATTERN = u"classdef< 'class' NAME ['(' ')'] colon=':' any >" - - def transform(self, node, results): - colon = results[u"colon"] - idx = node.children.index(colon) - if (node.children[idx-2].value == '(' and - node.children[idx-1].value == ')'): - del node.children[idx-2:idx] - idx -= 2 - insert_object(node, idx) - touch_import_top(u'builtins', 'object', node) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -u""" -Fixer for: -it.__next__() -> it.next(). -next(it) -> it.next(). -""" - -from lib2to3.pgen2 import token -from lib2to3.pygram import python_symbols as syms -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, Call, find_binding, Attr - -bind_warning = u"Calls to builtin next() possibly shadowed by global binding" - - -class FixNext(fixer_base.BaseFix): - - PATTERN = u""" - power< base=any+ trailer< '.' attr='__next__' > any* > - | - power< head='next' trailer< '(' arg=any ')' > any* > - | - classdef< 'class' base=any+ ':' - suite< any* - funcdef< 'def' - attr='__next__' - parameters< '(' NAME ')' > any+ > - any* > > - """ - - def transform(self, node, results): - assert results - - base = results.get(u"base") - attr = results.get(u"attr") - head = results.get(u"head") - arg_ = results.get(u"arg") - if arg_: - arg = arg_.clone() - head.replace(Attr(Name(unicode(arg),prefix=head.prefix), - Name(u"next"))) - arg_.remove() - elif base: - attr.replace(Name(u"next", prefix=attr.prefix)) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -u""" -Fixer for print: from __future__ import print_function. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixPrintfunction(fixer_base.BaseFix): - - # explicit = True - - PATTERN = u""" - power< 'print' trailer < '(' any* ')' > any* > - """ - - def transform(self, node, results): - future_import(u"print_function", node) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -u"""Fixer for - raise E(V).with_traceback(T) - to: - from future.utils import raise_ - ... - raise_(E, V, T) - -TODO: FIXME!! - -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Comma, Node, Leaf, token, syms - -class FixRaise(fixer_base.BaseFix): - - PATTERN = u""" - raise_stmt< 'raise' (power< name=any [trailer< '(' val=any* ')' >] - [trailer< '.' 'with_traceback' > trailer< '(' trc=any ')' >] > | any) ['from' chain=any] >""" - - def transform(self, node, results): - FIXME - name, val, trc = (results.get(u"name"), results.get(u"val"), results.get(u"trc")) - chain = results.get(u"chain") - if chain is not None: - self.warning(node, u"explicit exception chaining is not supported in Python 2") - chain.prev_sibling.remove() - chain.remove() - if trc is not None: - val = val[0] if val else Leaf(token.NAME, u"None") - val.prefix = trc.prefix = u" " - kids = [Leaf(token.NAME, u"raise"), name.clone(), Comma(), - val.clone(), Comma(), trc.clone()] - raise_stmt = Node(syms.raise_stmt, kids) - node.replace(raise_stmt) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -u"""Fixer for 'raise E(V).with_traceback(T)' -> 'raise E, V, T'""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Comma, Node, Leaf, token, syms - -class FixRaise(fixer_base.BaseFix): - - PATTERN = u""" - raise_stmt< 'raise' (power< name=any [trailer< '(' val=any* ')' >] - [trailer< '.' 'with_traceback' > trailer< '(' trc=any ')' >] > | any) ['from' chain=any] >""" - - def transform(self, node, results): - name, val, trc = (results.get(u"name"), results.get(u"val"), results.get(u"trc")) - chain = results.get(u"chain") - if chain is not None: - self.warning(node, u"explicit exception chaining is not supported in Python 2") - chain.prev_sibling.remove() - chain.remove() - if trc is not None: - val = val[0] if val else Leaf(token.NAME, u"None") - val.prefix = trc.prefix = u" " - kids = [Leaf(token.NAME, u"raise"), name.clone(), Comma(), - val.clone(), Comma(), trc.clone()] - raise_stmt = Node(syms.raise_stmt, kids) - node.replace(raise_stmt) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -u"""Fixer for 'g.throw(E(V).with_traceback(T))' -> 'g.throw(E, V, T)'""" - -from lib2to3 import fixer_base -from lib2to3.pytree import Node, Leaf -from lib2to3.pgen2 import token -from lib2to3.fixer_util import Comma - -class FixThrow(fixer_base.BaseFix): - - PATTERN = u""" - power< any trailer< '.' 'throw' > - trailer< '(' args=power< exc=any trailer< '(' val=any* ')' > - trailer< '.' 'with_traceback' > trailer< '(' trc=any ')' > > ')' > > - """ - - def transform(self, node, results): - syms = self.syms - exc, val, trc = (results[u"exc"], results[u"val"], results[u"trc"]) - val = val[0] if val else Leaf(token.NAME, u"None") - val.prefix = trc.prefix = u" " - kids = [exc.clone(), Comma(), val.clone(), Comma(), trc.clone()] - args = results[u"args"] - args.children = kids diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -u""" -Fixer for: -(a,)* *b (,c)* [,] = s -for (a,)* *b (,c)* [,] in d: ... -""" - -from lib2to3 import fixer_base -from itertools import count -from lib2to3.fixer_util import (Assign, Comma, Call, Newline, Name, - Number, token, syms, Node, Leaf) -from libfuturize.fixer_util import indentation, suitify, commatize -# from libfuturize.fixer_util import Assign, Comma, Call, Newline, Name, Number, indentation, suitify, commatize, token, syms, Node, Leaf - -def assignment_source(num_pre, num_post, LISTNAME, ITERNAME): - u""" - Accepts num_pre and num_post, which are counts of values - before and after the starg (not including the starg) - Returns a source fit for Assign() from fixer_util - """ - children = [] - pre = unicode(num_pre) - post = unicode(num_post) - # This code builds the assignment source from lib2to3 tree primitives. - # It's not very readable, but it seems like the most correct way to do it. - if num_pre > 0: - pre_part = Node(syms.power, [Name(LISTNAME), Node(syms.trailer, [Leaf(token.LSQB, u"["), Node(syms.subscript, [Leaf(token.COLON, u":"), Number(pre)]), Leaf(token.RSQB, u"]")])]) - children.append(pre_part) - children.append(Leaf(token.PLUS, u"+", prefix=u" ")) - main_part = Node(syms.power, [Leaf(token.LSQB, u"[", prefix=u" "), Name(LISTNAME), Node(syms.trailer, [Leaf(token.LSQB, u"["), Node(syms.subscript, [Number(pre) if num_pre > 0 else Leaf(1, u""), Leaf(token.COLON, u":"), Node(syms.factor, [Leaf(token.MINUS, u"-"), Number(post)]) if num_post > 0 else Leaf(1, u"")]), Leaf(token.RSQB, u"]"), Leaf(token.RSQB, u"]")])]) - children.append(main_part) - if num_post > 0: - children.append(Leaf(token.PLUS, u"+", prefix=u" ")) - post_part = Node(syms.power, [Name(LISTNAME, prefix=u" "), Node(syms.trailer, [Leaf(token.LSQB, u"["), Node(syms.subscript, [Node(syms.factor, [Leaf(token.MINUS, u"-"), Number(post)]), Leaf(token.COLON, u":")]), Leaf(token.RSQB, u"]")])]) - children.append(post_part) - source = Node(syms.arith_expr, children) - return source - -class FixUnpacking(fixer_base.BaseFix): - - PATTERN = u""" - expl=expr_stmt< testlist_star_expr< - pre=(any ',')* - star_expr< '*' name=NAME > - post=(',' any)* [','] > '=' source=any > | - impl=for_stmt< 'for' lst=exprlist< - pre=(any ',')* - star_expr< '*' name=NAME > - post=(',' any)* [','] > 'in' it=any ':' suite=any>""" - - def fix_explicit_context(self, node, results): - pre, name, post, source = (results.get(n) for n in (u"pre", u"name", u"post", u"source")) - pre = [n.clone() for n in pre if n.type == token.NAME] - name.prefix = u" " - post = [n.clone() for n in post if n.type == token.NAME] - target = [n.clone() for n in commatize(pre + [name.clone()] + post)] - # to make the special-case fix for "*z, = ..." correct with the least - # amount of modification, make the left-side into a guaranteed tuple - target.append(Comma()) - source.prefix = u"" - setup_line = Assign(Name(self.LISTNAME), Call(Name(u"list"), [source.clone()])) - power_line = Assign(target, assignment_source(len(pre), len(post), self.LISTNAME, self.ITERNAME)) - return setup_line, power_line - - def fix_implicit_context(self, node, results): - u""" - Only example of the implicit context is - a for loop, so only fix that. - """ - pre, name, post, it = (results.get(n) for n in (u"pre", u"name", u"post", u"it")) - pre = [n.clone() for n in pre if n.type == token.NAME] - name.prefix = u" " - post = [n.clone() for n in post if n.type == token.NAME] - target = [n.clone() for n in commatize(pre + [name.clone()] + post)] - # to make the special-case fix for "*z, = ..." correct with the least - # amount of modification, make the left-side into a guaranteed tuple - target.append(Comma()) - source = it.clone() - source.prefix = u"" - setup_line = Assign(Name(self.LISTNAME), Call(Name(u"list"), [Name(self.ITERNAME)])) - power_line = Assign(target, assignment_source(len(pre), len(post), self.LISTNAME, self.ITERNAME)) - return setup_line, power_line - - def transform(self, node, results): - u""" - a,b,c,d,e,f,*g,h,i = range(100) changes to - _3to2list = list(range(100)) - a,b,c,d,e,f,g,h,i, = _3to2list[:6] + [_3to2list[6:-2]] + _3to2list[-2:] - - and - - for a,b,*c,d,e in iter_of_iters: do_stuff changes to - for _3to2iter in iter_of_iters: - _3to2list = list(_3to2iter) - a,b,c,d,e, = _3to2list[:2] + [_3to2list[2:-2]] + _3to2list[-2:] - do_stuff - """ - self.LISTNAME = self.new_name(u"_3to2list") - self.ITERNAME = self.new_name(u"_3to2iter") - expl, impl = results.get(u"expl"), results.get(u"impl") - if expl is not None: - setup_line, power_line = self.fix_explicit_context(node, results) - setup_line.prefix = expl.prefix - power_line.prefix = indentation(expl.parent) - setup_line.append_child(Newline()) - parent = node.parent - i = node.remove() - parent.insert_child(i, power_line) - parent.insert_child(i, setup_line) - elif impl is not None: - setup_line, power_line = self.fix_implicit_context(node, results) - suitify(node) - suite = [k for k in node.children if k.type == syms.suite][0] - setup_line.prefix = u"" - power_line.prefix = suite.children[1].value - suite.children[2].prefix = indentation(suite.children[2]) - suite.insert_child(2, Newline()) - suite.insert_child(2, power_line) - suite.insert_child(2, Newline()) - suite.insert_child(2, setup_line) - results.get(u"lst").replace(Name(self.ITERNAME, prefix=u" ")) diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/fixes/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/fixes/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -import sys -from lib2to3 import refactor - -# The original set of these fixes comes from lib3to2 (https://bitbucket.org/amentajo/lib3to2): -fix_names = set([ - 'libpasteurize.fixes.fix_add_all__future__imports', # from __future__ import absolute_import etc. on separate lines - 'libpasteurize.fixes.fix_add_future_standard_library_import', # we force adding this import for now, even if it doesn't seem necessary to the fix_future_standard_library fixer, for ease of testing - # 'libfuturize.fixes.fix_order___future__imports', # consolidates to a single line to simplify testing -- UNFINISHED - 'libpasteurize.fixes.fix_future_builtins', # adds "from future.builtins import *" - 'libfuturize.fixes.fix_future_standard_library', # adds "from future import standard_library" - - 'libpasteurize.fixes.fix_annotations', - # 'libpasteurize.fixes.fix_bitlength', # ints have this in Py2.7 - # 'libpasteurize.fixes.fix_bool', # need a decorator or Mixin - # 'libpasteurize.fixes.fix_bytes', # leave bytes as bytes - # 'libpasteurize.fixes.fix_classdecorator', # available in - # Py2.6+ - # 'libpasteurize.fixes.fix_collections', hmmm ... - # 'libpasteurize.fixes.fix_dctsetcomp', # avail in Py27 - 'libpasteurize.fixes.fix_division', # yes - # 'libpasteurize.fixes.fix_except', # avail in Py2.6+ - # 'libpasteurize.fixes.fix_features', # ? - 'libpasteurize.fixes.fix_fullargspec', - # 'libpasteurize.fixes.fix_funcattrs', - 'libpasteurize.fixes.fix_getcwd', - 'libpasteurize.fixes.fix_imports', # adds "from future import standard_library" - 'libpasteurize.fixes.fix_imports2', - # 'libpasteurize.fixes.fix_input', - # 'libpasteurize.fixes.fix_int', - # 'libpasteurize.fixes.fix_intern', - # 'libpasteurize.fixes.fix_itertools', - 'libpasteurize.fixes.fix_kwargs', # yes, we want this - # 'libpasteurize.fixes.fix_memoryview', - # 'libpasteurize.fixes.fix_metaclass', # write a custom handler for - # this - # 'libpasteurize.fixes.fix_methodattrs', # __func__ and __self__ seem to be defined on Py2.7 already - 'libpasteurize.fixes.fix_newstyle', # yes, we want this: explicit inheritance from object. Without new-style classes in Py2, super() will break etc. - # 'libpasteurize.fixes.fix_next', # use a decorator for this - # 'libpasteurize.fixes.fix_numliterals', # prob not - # 'libpasteurize.fixes.fix_open', # huh? - # 'libpasteurize.fixes.fix_print', # no way - 'libpasteurize.fixes.fix_printfunction', # adds __future__ import print_function - # 'libpasteurize.fixes.fix_raise_', # TODO: get this working! - - # 'libpasteurize.fixes.fix_range', # nope - # 'libpasteurize.fixes.fix_reduce', - # 'libpasteurize.fixes.fix_setliteral', - # 'libpasteurize.fixes.fix_str', - # 'libpasteurize.fixes.fix_super', # maybe, if our magic super() isn't robust enough - 'libpasteurize.fixes.fix_throw', # yes, if Py3 supports it - # 'libpasteurize.fixes.fix_unittest', - 'libpasteurize.fixes.fix_unpacking', # yes, this is useful - # 'libpasteurize.fixes.fix_with' # way out of date - ]) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -# empty to make this a package diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/main.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/main.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/libpasteurize/main.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/libpasteurize/main.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -""" -pasteurize: automatic conversion of Python 3 code to clean 2/3 code -=================================================================== - -``pasteurize`` attempts to convert existing Python 3 code into source-compatible -Python 2 and 3 code. - -Use it like this on Python 3 code: - - $ pasteurize --verbose mypython3script.py - -This removes any Py3-only syntax (e.g. new metaclasses) and adds these -import lines: - - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - from __future__ import unicode_literals - from future import standard_library - standard_library.install_hooks() - from builtins import * - -To write changes to the files, use the -w flag. - -It also adds any other wrappers needed for Py2/3 compatibility. - -Note that separate stages are not available (or needed) when converting from -Python 3 with ``pasteurize`` as they are when converting from Python 2 with -``futurize``. - -The --all-imports option forces adding all ``__future__`` imports, -``builtins`` imports, and standard library aliases, even if they don't -seem necessary for the current state of each module. (This can simplify -testing, and can reduce the need to think about Py2 compatibility when editing -the code further.) - -""" - -from __future__ import (absolute_import, print_function, unicode_literals) - -import sys -import logging -import optparse -from lib2to3.main import main, warn, StdoutRefactoringTool -from lib2to3 import refactor - -from future import __version__ -from libpasteurize.fixes import fix_names - - -def main(args=None): - """Main program. - - Returns a suggested exit status (0, 1, 2). - """ - # Set up option parser - parser = optparse.OptionParser(usage="pasteurize [options] file|dir ...") - parser.add_option("-V", "--version", action="store_true", - help="Report the version number of pasteurize") - parser.add_option("-a", "--all-imports", action="store_true", - help="Adds all __future__ and future imports to each module") - parser.add_option("-f", "--fix", action="append", default=[], - help="Each FIX specifies a transformation; default: all") - parser.add_option("-j", "--processes", action="store", default=1, - type="int", help="Run 2to3 concurrently") - parser.add_option("-x", "--nofix", action="append", default=[], - help="Prevent a fixer from being run.") - parser.add_option("-l", "--list-fixes", action="store_true", - help="List available transformations") - # parser.add_option("-p", "--print-function", action="store_true", - # help="Modify the grammar so that print() is a function") - parser.add_option("-v", "--verbose", action="store_true", - help="More verbose logging") - parser.add_option("--no-diffs", action="store_true", - help="Don't show diffs of the refactoring") - parser.add_option("-w", "--write", action="store_true", - help="Write back modified files") - parser.add_option("-n", "--nobackups", action="store_true", default=False, - help="Don't write backups for modified files.") - - # Parse command line arguments - refactor_stdin = False - flags = {} - options, args = parser.parse_args(args) - fixer_pkg = 'libpasteurize.fixes' - avail_fixes = fix_names - flags["print_function"] = True - - if not options.write and options.no_diffs: - warn("not writing files and not printing diffs; that's not very useful") - if not options.write and options.nobackups: - parser.error("Can't use -n without -w") - if options.version: - print(__version__) - return 0 - if options.list_fixes: - print("Available transformations for the -f/--fix option:") - for fixname in sorted(avail_fixes): - print(fixname) - if not args: - return 0 - if not args: - print("At least one file or directory argument required.", - file=sys.stderr) - print("Use --help to show usage.", file=sys.stderr) - return 2 - if "-" in args: - refactor_stdin = True - if options.write: - print("Can't write to stdin.", file=sys.stderr) - return 2 - - # Set up logging handler - level = logging.DEBUG if options.verbose else logging.INFO - logging.basicConfig(format='%(name)s: %(message)s', level=level) - - # Initialize the refactoring tool - unwanted_fixes = set(fixer_pkg + ".fix_" + fix for fix in options.nofix) - - extra_fixes = set() - if options.all_imports: - prefix = 'libpasteurize.fixes.' - extra_fixes.add(prefix + 'fix_add_all__future__imports') - extra_fixes.add(prefix + 'fix_add_future_standard_library_import') - extra_fixes.add(prefix + 'fix_add_all_future_builtins') - - fixer_names = avail_fixes | extra_fixes - unwanted_fixes - - rt = StdoutRefactoringTool(sorted(fixer_names), flags, set(), - options.nobackups, not options.no_diffs) - - # Refactor all files and directories passed as arguments - if not rt.errors: - if refactor_stdin: - rt.refactor_stdin() - else: - try: - rt.refactor(args, options.write, None, - options.processes) - except refactor.MultiprocessingUnsupported: - assert options.processes > 1 - print("Sorry, -j isn't " \ - "supported on this platform.", file=sys.stderr) - return 1 - rt.summarize() - - # Return error status (0 if rt.errors is zero) - return int(bool(rt.errors)) - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/builtins/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/builtins/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/builtins/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/builtins/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -""" -A resurrection of some old functions from Python 2 for use in Python 3. These -should be used sparingly, to help with porting efforts, since code using them -is no longer standard Python 3 code. - -This module provides the following: - -1. Implementations of these builtin functions which have no equivalent on Py3: - -- apply -- chr -- cmp -- execfile - -2. Aliases: - -- intern <- sys.intern -- raw_input <- input -- reduce <- functools.reduce -- reload <- imp.reload -- unichr <- chr -- unicode <- str -- xrange <- range - -3. List-producing versions of the corresponding Python 3 iterator-producing functions: - -- filter -- map -- range -- zip - -4. Forward-ported Py2 types: - -- basestring -- dict -- str -- long -- unicode - -""" - -from future.utils import PY3 -from past.builtins.noniterators import (filter, map, range, reduce, zip) -# from past.builtins.misc import (ascii, hex, input, oct, open) -if PY3: - from past.types import (basestring, - olddict as dict, - oldstr as str, - long, - unicode) -else: - from __builtin__ import (basestring, dict, str, long, unicode) - -from past.builtins.misc import (apply, chr, cmp, execfile, intern, oct, - raw_input, reload, unichr, unicode, xrange) -from past import utils - - -if utils.PY3: - # We only import names that shadow the builtins on Py3. No other namespace - # pollution on Py3. - - # Only shadow builtins on Py3; no new names - __all__ = ['filter', 'map', 'range', 'reduce', 'zip', - 'basestring', 'dict', 'str', 'long', 'unicode', - 'apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input', - 'reload', 'unichr', 'xrange' - ] - -else: - # No namespace pollution on Py2 - __all__ = [] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/builtins/misc.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/builtins/misc.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/builtins/misc.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/builtins/misc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -from __future__ import unicode_literals -import sys -import inspect -from collections import Mapping - -from future.utils import PY3, exec_ - - -if PY3: - import builtins - - def apply(f, *args, **kw): - return f(*args, **kw) - - from past.builtins import str as oldstr - - def chr(i): - """ - Return a byte-string of one character with ordinal i; 0 <= i <= 256 - """ - return oldstr(bytes((i,))) - - def cmp(x, y): - """ - cmp(x, y) -> integer - - Return negative if xy. - """ - return (x > y) - (x < y) - - from sys import intern - - def oct(number): - """oct(number) -> string - - Return the octal representation of an integer - """ - return '0' + builtins.oct(number)[2:] - - raw_input = input - from imp import reload - unicode = str - unichr = chr - xrange = range -else: - import __builtin__ - apply = __builtin__.apply - chr = __builtin__.chr - cmp = __builtin__.cmp - execfile = __builtin__.execfile - intern = __builtin__.intern - oct = __builtin__.oct - raw_input = __builtin__.raw_input - reload = __builtin__.reload - unicode = __builtin__.unicode - unichr = __builtin__.unichr - xrange = __builtin__.xrange - - -if PY3: - def execfile(filename, myglobals=None, mylocals=None): - """ - Read and execute a Python script from a file in the given namespaces. - The globals and locals are dictionaries, defaulting to the current - globals and locals. If only globals is given, locals defaults to it. - """ - if myglobals is None: - # There seems to be no alternative to frame hacking here. - caller_frame = inspect.stack()[1] - myglobals = caller_frame[0].f_globals - mylocals = caller_frame[0].f_locals - elif mylocals is None: - # Only if myglobals is given do we set mylocals to it. - mylocals = myglobals - if not isinstance(myglobals, Mapping): - raise TypeError('globals must be a mapping') - if not isinstance(mylocals, Mapping): - raise TypeError('locals must be a mapping') - with open(filename, "rbU") as fin: - source = fin.read() - code = compile(source, filename, "exec") - exec_(code, myglobals, mylocals) - - -if PY3: - __all__ = ['apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input', - 'reload', 'unichr', 'unicode', 'xrange'] -else: - __all__ = [] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/builtins/noniterators.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/builtins/noniterators.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/builtins/noniterators.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/builtins/noniterators.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,273 +0,0 @@ -""" -This module is designed to be used as follows:: - - from past.builtins.noniterators import filter, map, range, reduce, zip - -And then, for example:: - - assert isinstance(range(5), list) - -The list-producing functions this brings in are:: - -- ``filter`` -- ``map`` -- ``range`` -- ``reduce`` -- ``zip`` - -""" - -from __future__ import division, absolute_import, print_function - -from itertools import chain, starmap -import itertools # since zip_longest doesn't exist on Py2 -from past.types import basestring -from past.utils import PY3 - - -def flatmap(f, items): - return chain.from_iterable(map(f, items)) - - -if PY3: - import builtins - - # list-producing versions of the major Python iterating functions - def oldfilter(*args): - """ - filter(function or None, sequence) -> list, tuple, or string - - Return those items of sequence for which function(item) is true. - If function is None, return the items that are true. If sequence - is a tuple or string, return the same type, else return a list. - """ - mytype = type(args[1]) - if isinstance(args[1], basestring): - return mytype().join(builtins.filter(*args)) - elif isinstance(args[1], (tuple, list)): - return mytype(builtins.filter(*args)) - else: - # Fall back to list. Is this the right thing to do? - return list(builtins.filter(*args)) - - # This is surprisingly difficult to get right. For example, the - # solutions here fail with the test cases in the docstring below: - # http://stackoverflow.com/questions/8072755/ - def oldmap(func, *iterables): - """ - map(function, sequence[, sequence, ...]) -> list - - Return a list of the results of applying the function to the - items of the argument sequence(s). If more than one sequence is - given, the function is called with an argument list consisting of - the corresponding item of each sequence, substituting None for - missing values when not all sequences have the same length. If - the function is None, return a list of the items of the sequence - (or a list of tuples if more than one sequence). - - Test cases: - >>> oldmap(None, 'hello world') - ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] - - >>> oldmap(None, range(4)) - [0, 1, 2, 3] - - More test cases are in past.tests.test_builtins. - """ - zipped = itertools.zip_longest(*iterables) - l = list(zipped) - if len(l) == 0: - return [] - if func is None: - result = l - else: - result = list(starmap(func, l)) - - # Inspect to see whether it's a simple sequence of tuples - try: - if max([len(item) for item in result]) == 1: - return list(chain.from_iterable(result)) - # return list(flatmap(func, result)) - except TypeError as e: - # Simple objects like ints have no len() - pass - return result - - ############################ - ### For reference, the source code for Py2.7 map function: - # static PyObject * - # builtin_map(PyObject *self, PyObject *args) - # { - # typedef struct { - # PyObject *it; /* the iterator object */ - # int saw_StopIteration; /* bool: did the iterator end? */ - # } sequence; - # - # PyObject *func, *result; - # sequence *seqs = NULL, *sqp; - # Py_ssize_t n, len; - # register int i, j; - # - # n = PyTuple_Size(args); - # if (n < 2) { - # PyErr_SetString(PyExc_TypeError, - # "map() requires at least two args"); - # return NULL; - # } - # - # func = PyTuple_GetItem(args, 0); - # n--; - # - # if (func == Py_None) { - # if (PyErr_WarnPy3k("map(None, ...) not supported in 3.x; " - # "use list(...)", 1) < 0) - # return NULL; - # if (n == 1) { - # /* map(None, S) is the same as list(S). */ - # return PySequence_List(PyTuple_GetItem(args, 1)); - # } - # } - # - # /* Get space for sequence descriptors. Must NULL out the iterator - # * pointers so that jumping to Fail_2 later doesn't see trash. - # */ - # if ((seqs = PyMem_NEW(sequence, n)) == NULL) { - # PyErr_NoMemory(); - # return NULL; - # } - # for (i = 0; i < n; ++i) { - # seqs[i].it = (PyObject*)NULL; - # seqs[i].saw_StopIteration = 0; - # } - # - # /* Do a first pass to obtain iterators for the arguments, and set len - # * to the largest of their lengths. - # */ - # len = 0; - # for (i = 0, sqp = seqs; i < n; ++i, ++sqp) { - # PyObject *curseq; - # Py_ssize_t curlen; - # - # /* Get iterator. */ - # curseq = PyTuple_GetItem(args, i+1); - # sqp->it = PyObject_GetIter(curseq); - # if (sqp->it == NULL) { - # static char errmsg[] = - # "argument %d to map() must support iteration"; - # char errbuf[sizeof(errmsg) + 25]; - # PyOS_snprintf(errbuf, sizeof(errbuf), errmsg, i+2); - # PyErr_SetString(PyExc_TypeError, errbuf); - # goto Fail_2; - # } - # - # /* Update len. */ - # curlen = _PyObject_LengthHint(curseq, 8); - # if (curlen > len) - # len = curlen; - # } - # - # /* Get space for the result list. */ - # if ((result = (PyObject *) PyList_New(len)) == NULL) - # goto Fail_2; - # - # /* Iterate over the sequences until all have stopped. */ - # for (i = 0; ; ++i) { - # PyObject *alist, *item=NULL, *value; - # int numactive = 0; - # - # if (func == Py_None && n == 1) - # alist = NULL; - # else if ((alist = PyTuple_New(n)) == NULL) - # goto Fail_1; - # - # for (j = 0, sqp = seqs; j < n; ++j, ++sqp) { - # if (sqp->saw_StopIteration) { - # Py_INCREF(Py_None); - # item = Py_None; - # } - # else { - # item = PyIter_Next(sqp->it); - # if (item) - # ++numactive; - # else { - # if (PyErr_Occurred()) { - # Py_XDECREF(alist); - # goto Fail_1; - # } - # Py_INCREF(Py_None); - # item = Py_None; - # sqp->saw_StopIteration = 1; - # } - # } - # if (alist) - # PyTuple_SET_ITEM(alist, j, item); - # else - # break; - # } - # - # if (!alist) - # alist = item; - # - # if (numactive == 0) { - # Py_DECREF(alist); - # break; - # } - # - # if (func == Py_None) - # value = alist; - # else { - # value = PyEval_CallObject(func, alist); - # Py_DECREF(alist); - # if (value == NULL) - # goto Fail_1; - # } - # if (i >= len) { - # int status = PyList_Append(result, value); - # Py_DECREF(value); - # if (status < 0) - # goto Fail_1; - # } - # else if (PyList_SetItem(result, i, value) < 0) - # goto Fail_1; - # } - # - # if (i < len && PyList_SetSlice(result, i, len, NULL) < 0) - # goto Fail_1; - # - # goto Succeed; - # - # Fail_1: - # Py_DECREF(result); - # Fail_2: - # result = NULL; - # Succeed: - # assert(seqs); - # for (i = 0; i < n; ++i) - # Py_XDECREF(seqs[i].it); - # PyMem_DEL(seqs); - # return result; - # } - - def oldrange(*args, **kwargs): - return list(builtins.range(*args, **kwargs)) - - def oldzip(*args, **kwargs): - return list(builtins.zip(*args, **kwargs)) - - filter = oldfilter - map = oldmap - range = oldrange - from functools import reduce - zip = oldzip - __all__ = ['filter', 'map', 'range', 'reduce', 'zip'] - -else: - import __builtin__ - # Python 2-builtin ranges produce lists - filter = __builtin__.filter - map = __builtin__.map - range = __builtin__.range - reduce = __builtin__.reduce - zip = __builtin__.zip - __all__ = [] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -# coding=utf-8 -""" -past: compatibility with Python 2 from Python 3 -=============================================== - -``past`` is a package to aid with Python 2/3 compatibility. Whereas ``future`` -contains backports of Python 3 constructs to Python 2, ``past`` provides -implementations of some Python 2 constructs in Python 3 and tools to import and -run Python 2 code in Python 3. It is intended to be used sparingly, as a way of -running old Python 2 code from Python 3 until the code is ported properly. - -Potential uses for libraries: - -- as a step in porting a Python 2 codebase to Python 3 (e.g. with the ``futurize`` script) -- to provide Python 3 support for previously Python 2-only libraries with the - same APIs as on Python 2 -- particularly with regard to 8-bit strings (the - ``past.builtins.str`` type). -- to aid in providing minimal-effort Python 3 support for applications using - libraries that do not yet wish to upgrade their code properly to Python 3, or - wish to upgrade it gradually to Python 3 style. - - -Here are some code examples that run identically on Python 3 and 2:: - - >>> from past.builtins import str as oldstr - - >>> philosopher = oldstr(u'\u5b54\u5b50'.encode('utf-8')) - >>> # This now behaves like a Py2 byte-string on both Py2 and Py3. - >>> # For example, indexing returns a Python 2-like string object, not - >>> # an integer: - >>> philosopher[0] - '\xe5' - >>> type(philosopher[0]) - - - >>> # List-producing versions of range, reduce, map, filter - >>> from past.builtins import range, reduce - >>> range(10) - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) - 15 - - >>> # Other functions removed in Python 3 are resurrected ... - >>> from past.builtins import execfile - >>> execfile('myfile.py') - - >>> from past.builtins import raw_input - >>> name = raw_input('What is your name? ') - What is your name? [cursor] - - >>> from past.builtins import reload - >>> reload(mymodule) # equivalent to imp.reload(mymodule) in Python 3 - - >>> from past.builtins import xrange - >>> for i in xrange(10): - ... pass - - -It also provides import hooks so you can import and use Python 2 modules like -this:: - - $ python3 - - >>> from past import autotranslate - >>> authotranslate('mypy2module') - >>> import mypy2module - -until the authors of the Python 2 modules have upgraded their code. Then, for -example:: - - >>> mypy2module.func_taking_py2_string(oldstr(b'abcd')) - - -Credits -------- - -:Author: Ed Schofield -:Sponsor: Python Charmers Pty Ltd, Australia: http://pythoncharmers.com - - -Licensing ---------- -Copyright 2013-2016 Python Charmers Pty Ltd, Australia. -The software is distributed under an MIT licence. See LICENSE.txt. -""" - - -from past.translation import install_hooks as autotranslate -from future import __version__, __copyright__, __license__ - -__title__ = 'past' -__author__ = 'Ed Schofield' - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/translation/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/translation/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/translation/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/translation/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,498 +0,0 @@ -# -*- coding: utf-8 -*- -""" -past.translation -================== - -The ``past.translation`` package provides an import hook for Python 3 which -transparently runs ``futurize`` fixers over Python 2 code on import to convert -print statements into functions, etc. - -It is intended to assist users in migrating to Python 3.x even if some -dependencies still only support Python 2.x. - -Usage ------ - -Once your Py2 package is installed in the usual module search path, the import -hook is invoked as follows: - - >>> from past import autotranslate - >>> autotranslate('mypackagename') - -Or: - - >>> autotranslate(['mypackage1', 'mypackage2']) - -You can unregister the hook using:: - - >>> from past.translation import remove_hooks - >>> remove_hooks() - -Author: Ed Schofield. -Inspired by and based on ``uprefix`` by Vinay M. Sajip. -""" - -import imp -import logging -import marshal -import os -import sys -import copy -from lib2to3.pgen2.parse import ParseError -from lib2to3.refactor import RefactoringTool - -from libfuturize import fixes - - -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) - -myfixes = (list(fixes.libfuturize_fix_names_stage1) + - list(fixes.lib2to3_fix_names_stage1) + - list(fixes.libfuturize_fix_names_stage2) + - list(fixes.lib2to3_fix_names_stage2)) - - -# We detect whether the code is Py2 or Py3 by applying certain lib2to3 fixers -# to it. If the diff is empty, it's Python 3 code. - -py2_detect_fixers = [ -# From stage 1: - 'lib2to3.fixes.fix_apply', - # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. and move to stage2 - 'lib2to3.fixes.fix_except', - 'lib2to3.fixes.fix_execfile', - 'lib2to3.fixes.fix_exitfunc', - 'lib2to3.fixes.fix_funcattrs', - 'lib2to3.fixes.fix_filter', - 'lib2to3.fixes.fix_has_key', - 'lib2to3.fixes.fix_idioms', - 'lib2to3.fixes.fix_import', # makes any implicit relative imports explicit. (Use with ``from __future__ import absolute_import) - 'lib2to3.fixes.fix_intern', - 'lib2to3.fixes.fix_isinstance', - 'lib2to3.fixes.fix_methodattrs', - 'lib2to3.fixes.fix_ne', - 'lib2to3.fixes.fix_numliterals', # turns 1L into 1, 0755 into 0o755 - 'lib2to3.fixes.fix_paren', - 'lib2to3.fixes.fix_print', - 'lib2to3.fixes.fix_raise', # uses incompatible with_traceback() method on exceptions - 'lib2to3.fixes.fix_renames', - 'lib2to3.fixes.fix_reduce', - # 'lib2to3.fixes.fix_set_literal', # this is unnecessary and breaks Py2.6 support - 'lib2to3.fixes.fix_repr', - 'lib2to3.fixes.fix_standarderror', - 'lib2to3.fixes.fix_sys_exc', - 'lib2to3.fixes.fix_throw', - 'lib2to3.fixes.fix_tuple_params', - 'lib2to3.fixes.fix_types', - 'lib2to3.fixes.fix_ws_comma', - 'lib2to3.fixes.fix_xreadlines', - -# From stage 2: - 'lib2to3.fixes.fix_basestring', - # 'lib2to3.fixes.fix_buffer', # perhaps not safe. Test this. - # 'lib2to3.fixes.fix_callable', # not needed in Py3.2+ - # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. - 'lib2to3.fixes.fix_exec', - # 'lib2to3.fixes.fix_future', # we don't want to remove __future__ imports - 'lib2to3.fixes.fix_getcwdu', - # 'lib2to3.fixes.fix_imports', # called by libfuturize.fixes.fix_future_standard_library - # 'lib2to3.fixes.fix_imports2', # we don't handle this yet (dbm) - # 'lib2to3.fixes.fix_input', - # 'lib2to3.fixes.fix_itertools', - # 'lib2to3.fixes.fix_itertools_imports', - 'lib2to3.fixes.fix_long', - # 'lib2to3.fixes.fix_map', - # 'lib2to3.fixes.fix_metaclass', # causes SyntaxError in Py2! Use the one from ``six`` instead - 'lib2to3.fixes.fix_next', - 'lib2to3.fixes.fix_nonzero', # TODO: add a decorator for mapping __bool__ to __nonzero__ - # 'lib2to3.fixes.fix_operator', # we will need support for this by e.g. extending the Py2 operator module to provide those functions in Py3 - 'lib2to3.fixes.fix_raw_input', - # 'lib2to3.fixes.fix_unicode', # strips off the u'' prefix, which removes a potentially helpful source of information for disambiguating unicode/byte strings - # 'lib2to3.fixes.fix_urllib', - 'lib2to3.fixes.fix_xrange', - # 'lib2to3.fixes.fix_zip', -] - - -class RTs: - """ - A namespace for the refactoring tools. This avoids creating these at - the module level, which slows down the module import. (See issue #117). - - There are two possible grammars: with or without the print statement. - Hence we have two possible refactoring tool implementations. - """ - _rt = None - _rtp = None - _rt_py2_detect = None - _rtp_py2_detect = None - - @staticmethod - def setup(): - """ - Call this before using the refactoring tools to create them on demand - if needed. - """ - if None in [RTs._rt, RTs._rtp]: - RTs._rt = RefactoringTool(myfixes) - RTs._rtp = RefactoringTool(myfixes, {'print_function': True}) - - - @staticmethod - def setup_detect_python2(): - """ - Call this before using the refactoring tools to create them on demand - if needed. - """ - if None in [RTs._rt_py2_detect, RTs._rtp_py2_detect]: - RTs._rt_py2_detect = RefactoringTool(py2_detect_fixers) - RTs._rtp_py2_detect = RefactoringTool(py2_detect_fixers, - {'print_function': True}) - - -# We need to find a prefix for the standard library, as we don't want to -# process any files there (they will already be Python 3). -# -# The following method is used by Sanjay Vinip in uprefix. This fails for -# ``conda`` environments: -# # In a non-pythonv virtualenv, sys.real_prefix points to the installed Python. -# # In a pythonv venv, sys.base_prefix points to the installed Python. -# # Outside a virtual environment, sys.prefix points to the installed Python. - -# if hasattr(sys, 'real_prefix'): -# _syslibprefix = sys.real_prefix -# else: -# _syslibprefix = getattr(sys, 'base_prefix', sys.prefix) - -# Instead, we use the portion of the path common to both the stdlib modules -# ``math`` and ``urllib``. - -def splitall(path): - """ - Split a path into all components. From Python Cookbook. - """ - allparts = [] - while True: - parts = os.path.split(path) - if parts[0] == path: # sentinel for absolute paths - allparts.insert(0, parts[0]) - break - elif parts[1] == path: # sentinel for relative paths - allparts.insert(0, parts[1]) - break - else: - path = parts[0] - allparts.insert(0, parts[1]) - return allparts - - -def common_substring(s1, s2): - """ - Returns the longest common substring to the two strings, starting from the - left. - """ - chunks = [] - path1 = splitall(s1) - path2 = splitall(s2) - for (dir1, dir2) in zip(path1, path2): - if dir1 != dir2: - break - chunks.append(dir1) - return os.path.join(*chunks) - -# _stdlibprefix = common_substring(math.__file__, urllib.__file__) - - -def detect_python2(source, pathname): - """ - Returns a bool indicating whether we think the code is Py2 - """ - RTs.setup_detect_python2() - try: - tree = RTs._rt_py2_detect.refactor_string(source, pathname) - except ParseError as e: - if e.msg != 'bad input' or e.value != '=': - raise - tree = RTs._rtp.refactor_string(source, pathname) - - if source != str(tree)[:-1]: # remove added newline - # The above fixers made changes, so we conclude it's Python 2 code - logger.debug('Detected Python 2 code: {0}'.format(pathname)) - with open('/tmp/original_code.py', 'w') as f: - f.write('### Original code (detected as py2): %s\n%s' % - (pathname, source)) - with open('/tmp/py2_detection_code.py', 'w') as f: - f.write('### Code after running py3 detection (from %s)\n%s' % - (pathname, str(tree)[:-1])) - return True - else: - logger.debug('Detected Python 3 code: {0}'.format(pathname)) - with open('/tmp/original_code.py', 'w') as f: - f.write('### Original code (detected as py3): %s\n%s' % - (pathname, source)) - try: - os.remove('/tmp/futurize_code.py') - except OSError: - pass - return False - - -class Py2Fixer(object): - """ - An import hook class that uses lib2to3 for source-to-source translation of - Py2 code to Py3. - """ - - # See the comments on :class:future.standard_library.RenameImport. - # We add this attribute here so remove_hooks() and install_hooks() can - # unambiguously detect whether the import hook is installed: - PY2FIXER = True - - def __init__(self): - self.found = None - self.base_exclude_paths = ['future', 'past'] - self.exclude_paths = copy.copy(self.base_exclude_paths) - self.include_paths = [] - - def include(self, paths): - """ - Pass in a sequence of module names such as 'plotrique.plotting' that, - if present at the leftmost side of the full package name, would - specify the module to be transformed from Py2 to Py3. - """ - self.include_paths += paths - - def exclude(self, paths): - """ - Pass in a sequence of strings such as 'mymodule' that, if - present at the leftmost side of the full package name, would cause - the module not to undergo any source transformation. - """ - self.exclude_paths += paths - - def find_module(self, fullname, path=None): - logger.debug('Running find_module: {0}...'.format(fullname)) - if '.' in fullname: - parent, child = fullname.rsplit('.', 1) - if path is None: - loader = self.find_module(parent, path) - mod = loader.load_module(parent) - path = mod.__path__ - fullname = child - - # Perhaps we should try using the new importlib functionality in Python - # 3.3: something like this? - # thing = importlib.machinery.PathFinder.find_module(fullname, path) - try: - self.found = imp.find_module(fullname, path) - except Exception as e: - logger.debug('Py2Fixer could not find {0}') - logger.debug('Exception was: {0})'.format(fullname, e)) - return None - self.kind = self.found[-1][-1] - if self.kind == imp.PKG_DIRECTORY: - self.pathname = os.path.join(self.found[1], '__init__.py') - elif self.kind == imp.PY_SOURCE: - self.pathname = self.found[1] - return self - - def transform(self, source): - # This implementation uses lib2to3, - # you can override and use something else - # if that's better for you - - # lib2to3 likes a newline at the end - RTs.setup() - source += '\n' - try: - tree = RTs._rt.refactor_string(source, self.pathname) - except ParseError as e: - if e.msg != 'bad input' or e.value != '=': - raise - tree = RTs._rtp.refactor_string(source, self.pathname) - # could optimise a bit for only doing str(tree) if - # getattr(tree, 'was_changed', False) returns True - return str(tree)[:-1] # remove added newline - - def load_module(self, fullname): - logger.debug('Running load_module for {0}...'.format(fullname)) - if fullname in sys.modules: - mod = sys.modules[fullname] - else: - if self.kind in (imp.PY_COMPILED, imp.C_EXTENSION, imp.C_BUILTIN, - imp.PY_FROZEN): - convert = False - # elif (self.pathname.startswith(_stdlibprefix) - # and 'site-packages' not in self.pathname): - # # We assume it's a stdlib package in this case. Is this too brittle? - # # Please file a bug report at https://github.com/PythonCharmers/python-future - # # if so. - # convert = False - # in theory, other paths could be configured to be excluded here too - elif any([fullname.startswith(path) for path in self.exclude_paths]): - convert = False - elif any([fullname.startswith(path) for path in self.include_paths]): - convert = True - else: - convert = False - if not convert: - logger.debug('Excluded {0} from translation'.format(fullname)) - mod = imp.load_module(fullname, *self.found) - else: - logger.debug('Autoconverting {0} ...'.format(fullname)) - mod = imp.new_module(fullname) - sys.modules[fullname] = mod - - # required by PEP 302 - mod.__file__ = self.pathname - mod.__name__ = fullname - mod.__loader__ = self - - # This: - # mod.__package__ = '.'.join(fullname.split('.')[:-1]) - # seems to result in "SystemError: Parent module '' not loaded, - # cannot perform relative import" for a package's __init__.py - # file. We use the approach below. Another option to try is the - # minimal load_module pattern from the PEP 302 text instead. - - # Is the test in the next line more or less robust than the - # following one? Presumably less ... - # ispkg = self.pathname.endswith('__init__.py') - - if self.kind == imp.PKG_DIRECTORY: - mod.__path__ = [ os.path.dirname(self.pathname) ] - mod.__package__ = fullname - else: - #else, regular module - mod.__path__ = [] - mod.__package__ = fullname.rpartition('.')[0] - - try: - cachename = imp.cache_from_source(self.pathname) - if not os.path.exists(cachename): - update_cache = True - else: - sourcetime = os.stat(self.pathname).st_mtime - cachetime = os.stat(cachename).st_mtime - update_cache = cachetime < sourcetime - # # Force update_cache to work around a problem with it being treated as Py3 code??? - # update_cache = True - if not update_cache: - with open(cachename, 'rb') as f: - data = f.read() - try: - code = marshal.loads(data) - except Exception: - # pyc could be corrupt. Regenerate it - update_cache = True - if update_cache: - if self.found[0]: - source = self.found[0].read() - elif self.kind == imp.PKG_DIRECTORY: - with open(self.pathname) as f: - source = f.read() - - if detect_python2(source, self.pathname): - source = self.transform(source) - with open('/tmp/futurized_code.py', 'w') as f: - f.write('### Futurized code (from %s)\n%s' % - (self.pathname, source)) - - code = compile(source, self.pathname, 'exec') - - dirname = os.path.dirname(cachename) - if not os.path.exists(dirname): - os.makedirs(dirname) - try: - with open(cachename, 'wb') as f: - data = marshal.dumps(code) - f.write(data) - except Exception: # could be write-protected - pass - exec(code, mod.__dict__) - except Exception as e: - # must remove module from sys.modules - del sys.modules[fullname] - raise # keep it simple - - if self.found[0]: - self.found[0].close() - return mod - -_hook = Py2Fixer() - - -def install_hooks(include_paths=(), exclude_paths=()): - if isinstance(include_paths, str): - include_paths = (include_paths,) - if isinstance(exclude_paths, str): - exclude_paths = (exclude_paths,) - assert len(include_paths) + len(exclude_paths) > 0, 'Pass at least one argument' - _hook.include(include_paths) - _hook.exclude(exclude_paths) - # _hook.debug = debug - enable = sys.version_info[0] >= 3 # enabled for all 3.x - if enable and _hook not in sys.meta_path: - sys.meta_path.insert(0, _hook) # insert at beginning. This could be made a parameter - - # We could return the hook when there are ways of configuring it - #return _hook - - -def remove_hooks(): - if _hook in sys.meta_path: - sys.meta_path.remove(_hook) - - -def detect_hooks(): - """ - Returns True if the import hooks are installed, False if not. - """ - return _hook in sys.meta_path - # present = any([hasattr(hook, 'PY2FIXER') for hook in sys.meta_path]) - # return present - - -class hooks(object): - """ - Acts as a context manager. Use like this: - - >>> from past import translation - >>> with translation.hooks(): - ... import mypy2module - >>> import requests # py2/3 compatible anyway - >>> # etc. - """ - def __enter__(self): - self.hooks_were_installed = detect_hooks() - install_hooks() - return self - - def __exit__(self, *args): - if not self.hooks_were_installed: - remove_hooks() - - -class suspend_hooks(object): - """ - Acts as a context manager. Use like this: - - >>> from past import translation - >>> translation.install_hooks() - >>> import http.client - >>> # ... - >>> with translation.suspend_hooks(): - >>> import requests # or others that support Py2/3 - - If the hooks were disabled before the context, they are not installed when - the context is left. - """ - def __enter__(self): - self.hooks_were_installed = detect_hooks() - remove_hooks() - return self - def __exit__(self, *args): - if self.hooks_were_installed: - install_hooks() - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/basestring.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/basestring.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/basestring.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/basestring.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -""" -An implementation of the basestring type for Python 3 - -Example use: - ->>> s = b'abc' ->>> assert isinstance(s, basestring) ->>> from past.types import str as oldstr ->>> s2 = oldstr(b'abc') ->>> assert isinstance(s2, basestring) - -""" - -import sys - -from past.utils import with_metaclass, PY2 - -if PY2: - str = unicode - -ver = sys.version_info[:2] - - -class BaseBaseString(type): - def __instancecheck__(cls, instance): - return isinstance(instance, (bytes, str)) - - def __subclasshook__(cls, thing): - # TODO: What should go here? - raise NotImplemented - - -class basestring(with_metaclass(BaseBaseString)): - """ - A minimal backport of the Python 2 basestring type to Py3 - """ - - -__all__ = ['basestring'] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -""" -Forward-ports of types from Python 2 for use with Python 3: - -- ``basestring``: equivalent to ``(str, bytes)`` in ``isinstance`` checks -- ``dict``: with list-producing .keys() etc. methods -- ``str``: bytes-like, but iterating over them doesn't product integers -- ``long``: alias of Py3 int with ``L`` suffix in the ``repr`` -- ``unicode``: alias of Py3 str with ``u`` prefix in the ``repr`` - -""" - -from past import utils - -if utils.PY2: - import __builtin__ - basestring = __builtin__.basestring - dict = __builtin__.dict - str = __builtin__.str - long = __builtin__.long - unicode = __builtin__.unicode - __all__ = [] -else: - from .basestring import basestring - from .olddict import olddict - from .oldstr import oldstr - long = int - unicode = str - # from .unicode import unicode - __all__ = ['basestring', 'olddict', 'oldstr', 'long', 'unicode'] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/olddict.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/olddict.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/olddict.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/olddict.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -""" -A dict subclass for Python 3 that behaves like Python 2's dict - -Example use: - ->>> from past.builtins import dict ->>> d1 = dict() # instead of {} for an empty dict ->>> d2 = dict(key1='value1', key2='value2') - -The keys, values and items methods now return lists on Python 3.x and there are -methods for iterkeys, itervalues, iteritems, and viewkeys etc. - ->>> for d in (d1, d2): -... assert isinstance(d.keys(), list) -... assert isinstance(d.values(), list) -... assert isinstance(d.items(), list) -""" - -import sys - -from past.utils import with_metaclass - - -_builtin_dict = dict -ver = sys.version_info[:2] - - -class BaseOldDict(type): - def __instancecheck__(cls, instance): - return isinstance(instance, _builtin_dict) - - -class olddict(with_metaclass(BaseOldDict, _builtin_dict)): - """ - A backport of the Python 3 dict object to Py2 - """ - iterkeys = _builtin_dict.keys - viewkeys = _builtin_dict.keys - - def keys(self): - return list(super(olddict, self).keys()) - - itervalues = _builtin_dict.values - viewvalues = _builtin_dict.values - - def values(self): - return list(super(olddict, self).values()) - - iteritems = _builtin_dict.items - viewitems = _builtin_dict.items - - def items(self): - return list(super(olddict, self).items()) - - def has_key(self, k): - """ - D.has_key(k) -> True if D has a key k, else False - """ - return k in self - - # def __new__(cls, *args, **kwargs): - # """ - # dict() -> new empty dictionary - # dict(mapping) -> new dictionary initialized from a mapping object's - # (key, value) pairs - # dict(iterable) -> new dictionary initialized as if via: - # d = {} - # for k, v in iterable: - # d[k] = v - # dict(**kwargs) -> new dictionary initialized with the name=value pairs - # in the keyword argument list. For example: dict(one=1, two=2) - - # """ - # - # if len(args) == 0: - # return super(olddict, cls).__new__(cls) - # # Was: elif isinstance(args[0], newbytes): - # # We use type() instead of the above because we're redefining - # # this to be True for all unicode string subclasses. Warning: - # # This may render newstr un-subclassable. - # elif type(args[0]) == olddict: - # return args[0] - # # elif isinstance(args[0], _builtin_dict): - # # value = args[0] - # else: - # value = args[0] - # return super(olddict, cls).__new__(cls, value) - - def __native__(self): - """ - Hook for the past.utils.native() function - """ - return super(oldbytes, self) - - -__all__ = ['olddict'] - diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/oldstr.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/oldstr.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/types/oldstr.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/types/oldstr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -""" -Pure-Python implementation of a Python 2-like str object for Python 3. -""" - -from collections import Iterable -from numbers import Integral - -from past.utils import PY2, with_metaclass - - -_builtin_bytes = bytes - - -class BaseOldStr(type): - def __instancecheck__(cls, instance): - return isinstance(instance, _builtin_bytes) - - -def unescape(s): - """ - Interprets strings with escape sequences - - Example: - >>> s = unescape(r'abc\\def') # i.e. 'abc\\\\def' - >>> print(s) - 'abc\def' - >>> s2 = unescape('abc\\ndef') - >>> len(s2) - 8 - >>> print(s2) - abc - def - """ - return s.encode().decode('unicode_escape') - - -class oldstr(with_metaclass(BaseOldStr, _builtin_bytes)): - """ - A forward port of the Python 2 8-bit string object to Py3 - """ - # Python 2 strings have no __iter__ method: - @property - def __iter__(self): - raise AttributeError - - def __dir__(self): - return [thing for thing in dir(_builtin_bytes) if thing != '__iter__'] - - # def __new__(cls, *args, **kwargs): - # """ - # From the Py3 bytes docstring: - - # bytes(iterable_of_ints) -> bytes - # bytes(string, encoding[, errors]) -> bytes - # bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer - # bytes(int) -> bytes object of size given by the parameter initialized with null bytes - # bytes() -> empty bytes object - # - # Construct an immutable array of bytes from: - # - an iterable yielding integers in range(256) - # - a text string encoded using the specified encoding - # - any object implementing the buffer API. - # - an integer - # """ - # - # if len(args) == 0: - # return super(newbytes, cls).__new__(cls) - # # Was: elif isinstance(args[0], newbytes): - # # We use type() instead of the above because we're redefining - # # this to be True for all unicode string subclasses. Warning: - # # This may render newstr un-subclassable. - # elif type(args[0]) == newbytes: - # return args[0] - # elif isinstance(args[0], _builtin_bytes): - # value = args[0] - # elif isinstance(args[0], unicode): - # if 'encoding' not in kwargs: - # raise TypeError('unicode string argument without an encoding') - # ### - # # Was: value = args[0].encode(**kwargs) - # # Python 2.6 string encode() method doesn't take kwargs: - # # Use this instead: - # newargs = [kwargs['encoding']] - # if 'errors' in kwargs: - # newargs.append(kwargs['errors']) - # value = args[0].encode(*newargs) - # ### - # elif isinstance(args[0], Iterable): - # if len(args[0]) == 0: - # # What is this? - # raise ValueError('unknown argument type') - # elif len(args[0]) > 0 and isinstance(args[0][0], Integral): - # # It's a list of integers - # value = b''.join([chr(x) for x in args[0]]) - # else: - # raise ValueError('item cannot be interpreted as an integer') - # elif isinstance(args[0], Integral): - # if args[0] < 0: - # raise ValueError('negative count') - # value = b'\x00' * args[0] - # else: - # value = args[0] - # return super(newbytes, cls).__new__(cls, value) - - def __repr__(self): - s = super(oldstr, self).__repr__() # e.g. b'abc' on Py3, b'abc' on Py3 - return s[1:] - - def __str__(self): - s = super(oldstr, self).__str__() # e.g. "b'abc'" or "b'abc\\ndef' - # TODO: fix this: - assert s[:2] == "b'" and s[-1] == "'" - return unescape(s[2:-1]) # e.g. 'abc' or 'abc\ndef' - - def __getitem__(self, y): - if isinstance(y, Integral): - return super(oldstr, self).__getitem__(slice(y, y+1)) - else: - return super(oldstr, self).__getitem__(y) - - def __getslice__(self, *args): - return self.__getitem__(slice(*args)) - - def __contains__(self, key): - if isinstance(key, int): - return False - - def __native__(self): - return bytes(self) - - -__all__ = ['oldstr'] diff -Nru pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/utils/__init__.py pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/utils/__init__.py --- pyglet-1.4.10/pyglet/extlibs/future/py2_3/past/utils/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/future/py2_3/past/utils/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -""" -Various non-built-in utility functions and definitions for Py2 -compatibility in Py3. - -For example: - - >>> # The old_div() function behaves like Python 2's / operator - >>> # without "from __future__ import division" - >>> from past.utils import old_div - >>> old_div(3, 2) # like 3/2 in Py2 - 0 - >>> old_div(3, 2.0) # like 3/2.0 in Py2 - 1.5 -""" - -import sys -import numbers - -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 -PYPY = hasattr(sys, 'pypy_translation_info') - - -def with_metaclass(meta, *bases): - """ - Function from jinja2/_compat.py. License: BSD. - - Use it like this:: - - class BaseForm(object): - pass - - class FormType(type): - pass - - class Form(with_metaclass(FormType, BaseForm)): - pass - - 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. Because of internal type checks - we also need to make sure that we downgrade the custom metaclass - for one level to something closer to type (that's why __call__ and - __init__ comes back from type etc.). - - This has the advantage over six.with_metaclass of not introducing - dummy classes into the final MRO. - """ - class metaclass(meta): - __call__ = type.__call__ - __init__ = type.__init__ - def __new__(cls, name, this_bases, d): - if this_bases is None: - return type.__new__(cls, name, (), d) - return meta(name, bases, d) - return metaclass('temporary_class', None, {}) - - -def native(obj): - """ - On Py2, this is a no-op: native(obj) -> obj - - On Py3, returns the corresponding native Py3 types that are - superclasses for forward-ported objects from Py2: - - >>> from past.builtins import str, dict - - >>> native(str(b'ABC')) # Output on Py3 follows. On Py2, output is 'ABC' - b'ABC' - >>> type(native(str(b'ABC'))) - bytes - - Existing native types on Py3 will be returned unchanged: - - >>> type(native(b'ABC')) - bytes - """ - if hasattr(obj, '__native__'): - return obj.__native__() - else: - return obj - - -# An alias for future.utils.old_div(): -def old_div(a, b): - """ - Equivalent to ``a / b`` on Python 2 without ``from __future__ import - division``. - - TODO: generalize this to other objects (like arrays etc.) - """ - if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral): - return a // b - else: - return a / b - -__all__ = ['PY3', 'PY2', 'PYPY', 'with_metaclass', 'native', 'old_div'] diff -Nru pyglet-1.4.10/pyglet/extlibs/png.py pyglet-1.5.14/pyglet/extlibs/png.py --- pyglet-1.4.10/pyglet/extlibs/png.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/extlibs/png.py 2020-12-23 15:47:40.000000000 +0000 @@ -369,7 +369,7 @@ pass -class Writer(object): +class Writer: """ PNG encoder in pure Python. """ @@ -1291,7 +1291,7 @@ # So that refugee's from PIL feel more at home. Not documented. fromarray = from_array -class Image(object): +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. @@ -1332,7 +1332,7 @@ finally: close() -class _readable(object): +class _readable: """ A simple file-like interface for strings and arrays. """ @@ -1356,7 +1356,7 @@ def as_str(x): return str(x, 'ascii') -class Reader(object): +class Reader: """ PNG decoder in pure Python. """ @@ -2297,7 +2297,7 @@ try: pngfilters except NameError: - class pngfilters(object): + class pngfilters: def undo_filter_sub(filter_unit, scanline, previous, result): """Undo sub filter.""" diff -Nru pyglet-1.4.10/pyglet/font/base.py pyglet-1.5.14/pyglet/font/base.py --- pyglet-1.4.10/pyglet/font/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/base.py 2020-12-23 15:47:40.000000000 +0000 @@ -39,14 +39,6 @@ in `pyglet.font` to obtain platform-specific instances. You can use these classes as a documented interface to the concrete classes. """ -from builtins import chr -from builtins import str -from builtins import map -from builtins import range -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' import unicodedata @@ -256,7 +248,7 @@ return region -class GlyphRenderer(object): +class GlyphRenderer: """Abstract class for creating glyph images. """ def __init__(self, font): @@ -272,7 +264,7 @@ pass -class Font(object): +class Font: """Abstract font class able to produce glyphs. To construct a font, use :py:func:`pyglet.font.load`, which will instantiate the diff -Nru pyglet-1.4.10/pyglet/font/fontconfig.py pyglet-1.5.14/pyglet/font/fontconfig.py --- pyglet-1.4.10/pyglet/font/fontconfig.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/fontconfig.py 2020-11-10 10:36:26.000000000 +0000 @@ -35,18 +35,15 @@ """ Wrapper around the Linux FontConfig library. Used to find available fonts. """ -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' from collections import OrderedDict from ctypes import * import pyglet.lib -from pyglet.compat import asbytes, asstr +from pyglet.util import asbytes, asstr from pyglet.font.base import FontException + # fontconfig library definitions (FcResultMatch, @@ -108,7 +105,7 @@ # End of library definitions -class FontConfig(object): +class FontConfig: def __init__(self): self._fontconfig = self._load_fontconfig_library() self._search_cache = OrderedDict() @@ -193,7 +190,7 @@ return fontconfig -class FontConfigPattern(object): +class FontConfigPattern: def __init__(self, fontconfig, pattern=None): self._fontconfig = fontconfig self._pattern = pattern diff -Nru pyglet-1.4.10/pyglet/font/freetype_lib.py pyglet-1.5.14/pyglet/font/freetype_lib.py --- pyglet-1.4.10/pyglet/font/freetype_lib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/freetype_lib.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,8 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import print_function -from __future__ import absolute_import from ctypes import * from .base import FontException @@ -43,6 +41,7 @@ _font_data = {} + def _get_function(name, argtypes, rtype): try: func = getattr(_libfreetype, name) @@ -50,7 +49,7 @@ func.restype = rtype return func except AttributeError as e: - raise ImportError(e) + raise ImportError(e) FT_Byte = c_char diff -Nru pyglet-1.4.10/pyglet/font/freetype.py pyglet-1.5.14/pyglet/font/freetype.py --- pyglet-1.4.10/pyglet/font/freetype.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/freetype.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,13 +32,11 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import division -from builtins import object import ctypes from collections import namedtuple -from pyglet.compat import asbytes, asstr +from pyglet.util import asbytes, asstr from pyglet.font import base from pyglet import image from pyglet.font.fontconfig import get_fontconfig @@ -140,7 +138,7 @@ ['ascent', 'descent']) -class MemoryFaceStore(object): +class MemoryFaceStore: def __init__(self): self._dict = {} @@ -211,7 +209,7 @@ cls._memory_faces.add(face) -class FreeTypeFace(object): +class FreeTypeFace: """FreeType typographic face object. Keeps the reference count to the face at +1 as long as this object exists. If other objects diff -Nru pyglet-1.4.10/pyglet/font/__init__.py pyglet-1.5.14/pyglet/font/__init__.py --- pyglet-1.4.10/pyglet/font/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -50,8 +50,6 @@ See the :mod:`pyglet.font.base` module for documentation on the base classes used by this package. """ -from __future__ import absolute_import, division -from builtins import str import os import sys diff -Nru pyglet-1.4.10/pyglet/font/quartz.py pyglet-1.5.14/pyglet/font/quartz.py --- pyglet-1.4.10/pyglet/font/quartz.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/quartz.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,16 +33,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from builtins import map -from builtins import str - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -# TODO Tiger and later: need to set kWindowApplicationScaledAttribute for DPI -# independence? +# TODO Tiger and later: need to set kWindowApplicationScaledAttribute for DPI independence? import math from ctypes import c_void_p, c_int32, byref, c_byte @@ -56,6 +47,7 @@ ct = cocoapy.ct quartz = cocoapy.quartz + class QuartzGlyphRenderer(base.GlyphRenderer): def __init__(self, font): super(QuartzGlyphRenderer, self).__init__(font) @@ -177,7 +169,6 @@ # Otherwise return whatever we have. return list(fonts.values())[0] - def _create_font_descriptor(self, family_name, traits): # Create an attribute dictionary. attributes = c_void_p(cf.CFDictionaryCreateMutable(None, 0, cf.kCFTypeDictionaryKeyCallBacks, cf.kCFTypeDictionaryValueCallBacks)) @@ -214,8 +205,10 @@ # Construct traits value. traits = 0 - if bold: traits |= cocoapy.kCTFontBoldTrait - if italic: traits |= cocoapy.kCTFontItalicTrait + if bold: + traits |= cocoapy.kCTFontBoldTrait + if italic: + traits |= cocoapy.kCTFontItalicTrait name = str(name) # First see if we can find an appropriate font from our table of loaded fonts. diff -Nru pyglet-1.4.10/pyglet/font/ttf.py pyglet-1.5.14/pyglet/font/ttf.py --- pyglet-1.4.10/pyglet/font/ttf.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/ttf.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id$ """ Implementation of the Truetype file format. @@ -44,22 +43,14 @@ * http://developer.apple.com/fonts/TTRefMan/RM06 * http://www.microsoft.com/typography/otspec """ -from __future__ import division -from builtins import zip -from builtins import chr -from builtins import range -from builtins import object -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -import codecs import os import mmap import struct +import codecs -class TruetypeInfo(object): +class TruetypeInfo: """Information about a single Truetype face. The class memory-maps the font file to read the tables, so @@ -472,7 +463,7 @@ names.append(name) fmt += entry_type - class TableClass(object): + class TableClass: size = struct.calcsize(fmt) def __init__(self, data, offset): diff -Nru pyglet-1.4.10/pyglet/font/win32.py pyglet-1.5.14/pyglet/font/win32.py --- pyglet-1.4.10/pyglet/font/win32.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/win32.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,14 +33,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' +# TODO Windows Vista: need to call SetProcessDPIAware? May affect GDI+ calls as well as font. -# TODO Windows Vista: need to call SetProcessDPIAware? May affect GDI+ calls -# as well as font. - -from ctypes import * -import ctypes import math from sys import byteorder @@ -52,7 +46,7 @@ from pyglet.libs.win32.types import * from pyglet.libs.win32 import _gdi32 as gdi32, _user32 as user32 from pyglet.libs.win32 import _kernel32 as kernel32 -from pyglet.compat import asbytes +from pyglet.util import asbytes _debug_font = pyglet.options['debug_font'] diff -Nru pyglet-1.4.10/pyglet/font/win32query.py pyglet-1.5.14/pyglet/font/win32query.py --- pyglet-1.4.10/pyglet/font/win32query.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/font/win32query.py 2020-11-10 10:36:26.000000000 +0000 @@ -105,8 +105,7 @@ CreateFontIndirect. """ -from __future__ import print_function -from builtins import object + DEBUG = False __all__ = ['have_font', 'font_list'] @@ -114,32 +113,31 @@ __version__ = '0.3' __url__ = 'https://bitbucket.org/techtonik/fontquery' -import sys -PY3K = sys.version_info >= (3, 0) -#-- INTRO: MAINTAIN CACHED FONTS DB -- +# -- INTRO: MAINTAIN CACHED FONTS DB -- # [ ] make it Django/NDB style model definition -class FontEntry(object): - """ - Font classification. - Level 0: - - name - - vector (True if font is vector, False for raster fonts) - - format: ttf | ... - """ - def __init__(self, name, vector, format, monospace, family): - self.name = name - self.vector = vector - self.format = format - self.monospace = monospace - self.family = family +class FontEntry: + """ + Font classification. + Level 0: + - name + - vector (True if font is vector, False for raster fonts) + - format: ttf | ... + """ + + def __init__(self, name, vector, format, monospace, family): + self.name = name + self.vector = vector + self.format = format + self.monospace = monospace + self.family = family + # List of FontEntry objects FONTDB = [] - -#-- CHAPTER 1: GET ALL SYSTEM FONTS USING EnumFontFamiliesEx FROM GDI -- +# -- CHAPTER 1: GET ALL SYSTEM FONTS USING EnumFontFamiliesEx FROM GDI -- """ Q: Why GDI? Why not GDI+? @@ -163,39 +161,39 @@ # for calling ANSI functions of Windows API (end with A) TCHAR is # defined as single char, for Unicode ones (end witn W) it is WCHAR -CHAR = ctypes.c_char # Python 2.7 compatibility +CHAR = ctypes.c_char # Python 2.7 compatibility TCHAR = CHAR -BYTE = ctypes.c_ubyte # http://bugs.python.org/issue16376 +BYTE = ctypes.c_ubyte # http://bugs.python.org/issue16376 # charset codes for LOGFONT structure -ANSI_CHARSET = 0 -ARABIC_CHARSET = 178 -BALTIC_CHARSET = 186 +ANSI_CHARSET = 0 +ARABIC_CHARSET = 178 +BALTIC_CHARSET = 186 CHINESEBIG5_CHARSET = 136 DEFAULT_CHARSET = 1 - # - charset for current system locale - - # means function can be called several times - # for the single font (for each charset) +# - charset for current system locale - +# means function can be called several times +# for the single font (for each charset) EASTEUROPE_CHARSET = 238 GB2312_CHARSET = 134 -GREEK_CHARSET = 161 +GREEK_CHARSET = 161 HANGUL_CHARSET = 129 HEBREW_CHARSET = 177 -JOHAB_CHARSET = 130 +JOHAB_CHARSET = 130 MAC_CHARSET = 77 OEM_CHARSET = 255 # OS dependent system charset -RUSSIAN_CHARSET = 204 +RUSSIAN_CHARSET = 204 SHIFTJIS_CHARSET = 128 -SYMBOL_CHARSET = 2 -THAI_CHARSET = 222 -TURKISH_CHARSET = 162 +SYMBOL_CHARSET = 2 +THAI_CHARSET = 222 +TURKISH_CHARSET = 162 VIETNAMESE_CHARSET = 163 # build lookup dictionary to get charset name from its code CHARSET_NAMES = {} for (name, value) in locals().copy().items(): - if name.endswith('_CHARSET'): - CHARSET_NAMES[value] = name + if name.endswith('_CHARSET'): + CHARSET_NAMES[value] = name # font pitch constants ('fixed pitch' means 'monospace') DEFAULT_PITCH = 0 @@ -203,329 +201,333 @@ VARIABLE_PITCH = 2 # Windows font family constants -FF_DONTCARE = 0 # Don't care or don't know -FF_ROMAN = 1 # with serifs, proportional -FF_SWISS = 2 # w/out serifs, proportional -FF_MODERN = 3 # constant stroke width -FF_SCRIPT = 4 # handwritten -FF_DECORATIVE = 5 # novelty +FF_DONTCARE = 0 # Don't care or don't know +FF_ROMAN = 1 # with serifs, proportional +FF_SWISS = 2 # w/out serifs, proportional +FF_MODERN = 3 # constant stroke width +FF_SCRIPT = 4 # handwritten +FF_DECORATIVE = 5 # novelty class LOGFONT(ctypes.Structure): - # EnumFontFamiliesEx examines only 3 fields: - # - lfCharSet - # - lfFaceName - empty string enumerates one font in each available - # typeface name, valid typeface name gets all fonts - # with that name - # - lfPitchAndFamily - must be set to 0 [ ] - _fields_ = [ - ('lfHeight', wintypes.LONG), - # value > 0 specifies the largest size of *char cell* to match - # char cell = char height + internal leading - # value = 0 makes matched use default height for search - # value < 0 specifies the largest size of *char height* to match - ('lfWidth', wintypes.LONG), - # average width also in *logical units*, which are pixels in - # default _mapping mode_ (MM_TEXT) for device - ('lfEscapement', wintypes.LONG), - # string baseline rotation in tenths of degrees - ('lfOrientation', wintypes.LONG), - # character rotation in tenths of degrees - ('lfWeight', wintypes.LONG), - # 0 through 1000 400 is normal, 700 is bold, 0 is default - ('lfItalic', BYTE), - ('lfUnderline', BYTE), - ('lfStrikeOut', BYTE), - ('lfCharSet', BYTE), - # ANSI_CHARSET, BALTIC_CHARSET, ... - see *_CHARSET constants above - ('lfOutPrecision', BYTE), - # many constants how the output must match height, width, pitch etc. - # OUT_DEFAULT_PRECIS - # [ ] TODO - ('lfClipPrecision', BYTE), - # how to clip characters, no useful properties, leave default value - # CLIP_DEFAULT_PRECIS - ('lfQuality', BYTE), - # ANTIALIASED_QUALITY - # CLEARTYPE_QUALITY - # DEFAULT_QUALITY - # DRAFT_QUALITY - # NONANTIALIASED_QUALITY - # PROOF_QUALITY - ('lfPitchAndFamily', BYTE), - # DEFAULT_PITCH - # FIXED_PITCH - authoritative for monospace - # VARIABLE_PITCH - # stacked with any of - # FF_DECORATIVE - novelty - # FF_DONTCARE - default font - # FF_MODERN - stroke width ('pen width') near constant - # FF_ROMAN - proportional (variable char width) with serifs - # FF_SCRIPT - handwritten - # FF_SWISS - proportional without serifs - ('lfFaceName', TCHAR*32)] - # typeface name of the font - null-terminated string + # EnumFontFamiliesEx examines only 3 fields: + # - lfCharSet + # - lfFaceName - empty string enumerates one font in each available + # typeface name, valid typeface name gets all fonts + # with that name + # - lfPitchAndFamily - must be set to 0 [ ] + _fields_ = [ + ('lfHeight', wintypes.LONG), + # value > 0 specifies the largest size of *char cell* to match + # char cell = char height + internal leading + # value = 0 makes matched use default height for search + # value < 0 specifies the largest size of *char height* to match + ('lfWidth', wintypes.LONG), + # average width also in *logical units*, which are pixels in + # default _mapping mode_ (MM_TEXT) for device + ('lfEscapement', wintypes.LONG), + # string baseline rotation in tenths of degrees + ('lfOrientation', wintypes.LONG), + # character rotation in tenths of degrees + ('lfWeight', wintypes.LONG), + # 0 through 1000 400 is normal, 700 is bold, 0 is default + ('lfItalic', BYTE), + ('lfUnderline', BYTE), + ('lfStrikeOut', BYTE), + ('lfCharSet', BYTE), + # ANSI_CHARSET, BALTIC_CHARSET, ... - see *_CHARSET constants above + ('lfOutPrecision', BYTE), + # many constants how the output must match height, width, pitch etc. + # OUT_DEFAULT_PRECIS + # [ ] TODO + ('lfClipPrecision', BYTE), + # how to clip characters, no useful properties, leave default value + # CLIP_DEFAULT_PRECIS + ('lfQuality', BYTE), + # ANTIALIASED_QUALITY + # CLEARTYPE_QUALITY + # DEFAULT_QUALITY + # DRAFT_QUALITY + # NONANTIALIASED_QUALITY + # PROOF_QUALITY + ('lfPitchAndFamily', BYTE), + # DEFAULT_PITCH + # FIXED_PITCH - authoritative for monospace + # VARIABLE_PITCH + # stacked with any of + # FF_DECORATIVE - novelty + # FF_DONTCARE - default font + # FF_MODERN - stroke width ('pen width') near constant + # FF_ROMAN - proportional (variable char width) with serifs + # FF_SCRIPT - handwritten + # FF_SWISS - proportional without serifs + ('lfFaceName', TCHAR * 32)] + # typeface name of the font - null-terminated string + class FONTSIGNATURE(ctypes.Structure): - # supported code pages and Unicode subranges for the font - # needed for NEWTEXTMETRICEX structure - _fields_ = [ - ('sUsb', wintypes.DWORD*4), # 128-bit Unicode subset bitfield (USB) - ('sCsb', wintypes.DWORD*2)] # 64-bit, code-page bitfield (CPB) + # supported code pages and Unicode subranges for the font + # needed for NEWTEXTMETRICEX structure + _fields_ = [ + ('sUsb', wintypes.DWORD * 4), # 128-bit Unicode subset bitfield (USB) + ('sCsb', wintypes.DWORD * 2)] # 64-bit, code-page bitfield (CPB) + class NEWTEXTMETRIC(ctypes.Structure): - # physical font attributes for True Type fonts - # needed for NEWTEXTMETRICEX structure - _fields_ = [ - ('tmHeight', wintypes.LONG), - ('tmAscent', wintypes.LONG), - ('tmDescent', wintypes.LONG), - ('tmInternalLeading', wintypes.LONG), - ('tmExternalLeading', wintypes.LONG), - ('tmAveCharWidth', wintypes.LONG), - ('tmMaxCharWidth', wintypes.LONG), - ('tmWeight', wintypes.LONG), - ('tmOverhang', wintypes.LONG), - ('tmDigitizedAspectX', wintypes.LONG), - ('tmDigitizedAspectY', wintypes.LONG), - ('mFirstChar', TCHAR), - ('mLastChar', TCHAR), - ('mDefaultChar', TCHAR), - ('mBreakChar', TCHAR), - ('tmItalic', BYTE), - ('tmUnderlined', BYTE), - ('tmStruckOut', BYTE), - ('tmPitchAndFamily', BYTE), - ('tmCharSet', BYTE), - ('tmFlags', wintypes.DWORD), - ('ntmSizeEM', wintypes.UINT), - ('ntmCellHeight', wintypes.UINT), - ('ntmAvgWidth', wintypes.UINT)] + # physical font attributes for True Type fonts + # needed for NEWTEXTMETRICEX structure + _fields_ = [ + ('tmHeight', wintypes.LONG), + ('tmAscent', wintypes.LONG), + ('tmDescent', wintypes.LONG), + ('tmInternalLeading', wintypes.LONG), + ('tmExternalLeading', wintypes.LONG), + ('tmAveCharWidth', wintypes.LONG), + ('tmMaxCharWidth', wintypes.LONG), + ('tmWeight', wintypes.LONG), + ('tmOverhang', wintypes.LONG), + ('tmDigitizedAspectX', wintypes.LONG), + ('tmDigitizedAspectY', wintypes.LONG), + ('mFirstChar', TCHAR), + ('mLastChar', TCHAR), + ('mDefaultChar', TCHAR), + ('mBreakChar', TCHAR), + ('tmItalic', BYTE), + ('tmUnderlined', BYTE), + ('tmStruckOut', BYTE), + ('tmPitchAndFamily', BYTE), + ('tmCharSet', BYTE), + ('tmFlags', wintypes.DWORD), + ('ntmSizeEM', wintypes.UINT), + ('ntmCellHeight', wintypes.UINT), + ('ntmAvgWidth', wintypes.UINT)] + class NEWTEXTMETRICEX(ctypes.Structure): - # physical font attributes for True Type fonts - # needed for FONTENUMPROC callback function - _fields_ = [ - ('ntmTm', NEWTEXTMETRIC), - ('ntmFontSig', FONTSIGNATURE)] + # physical font attributes for True Type fonts + # needed for FONTENUMPROC callback function + _fields_ = [ + ('ntmTm', NEWTEXTMETRIC), + ('ntmFontSig', FONTSIGNATURE)] # type for a function that is called by the system for # each font during execution of EnumFontFamiliesEx FONTENUMPROC = ctypes.WINFUNCTYPE( - ctypes.c_int, # return non-0 to continue enumeration, 0 to stop - ctypes.POINTER(LOGFONT), - ctypes.POINTER(NEWTEXTMETRICEX), - wintypes.DWORD, # font type, a combination of - # DEVICE_FONTTYPE - # RASTER_FONTTYPE - # TRUETYPE_FONTTYPE - wintypes.LPARAM + ctypes.c_int, # return non-0 to continue enumeration, 0 to stop + ctypes.POINTER(LOGFONT), + ctypes.POINTER(NEWTEXTMETRICEX), + wintypes.DWORD, # font type, a combination of + # DEVICE_FONTTYPE + # RASTER_FONTTYPE + # TRUETYPE_FONTTYPE + wintypes.LPARAM ) # When running 64 bit windows, some types are not 32 bit, so Python/ctypes guesses wrong gdi32.EnumFontFamiliesExA.argtypes = [ - wintypes.HDC, - ctypes.POINTER(LOGFONT), - FONTENUMPROC, - wintypes.LPARAM, - wintypes.DWORD] + wintypes.HDC, + ctypes.POINTER(LOGFONT), + FONTENUMPROC, + wintypes.LPARAM, + wintypes.DWORD] def _enum_font_names(logfont, textmetricex, fonttype, param): - """callback function to be executed during EnumFontFamiliesEx - call for each font name. it stores names in global variable - """ - global FONTDB - - lf = logfont.contents - name = lf.lfFaceName - if PY3K: - # [ ] check this works - name = name.decode('utf-8') - - # detect font type (vector|raster) and format (ttf) - # [ ] use Windows constant TRUETYPE_FONTTYPE - if fonttype & 4: - vector = True - format = 'ttf' - else: - vector = False - # [ ] research Windows raster format structure - format = 'unknown' - - pitch = lf.lfPitchAndFamily & 0b11 - family = lf.lfPitchAndFamily >> 4 - - # [ ] check FIXED_PITCH, VARIABLE_PITCH and FF_MODERN - # combination - # - # FP T NM 400 CHARSET: 0 DFKai-SB - # FP T NM 400 CHARSET: 136 DFKai-SB - # FP T NM 400 CHARSET: 0 @DFKai-SB - # FP T NM 400 CHARSET: 136 @DFKai-SB - # VP T M 400 CHARSET: 0 OCR A Extended - - monospace = (pitch == FIXED_PITCH) - - charset = lf.lfCharSet - - FONTDB.append(FontEntry(name, vector, format, monospace, family)) - - if DEBUG: - info = '' - - if pitch == FIXED_PITCH: - info += 'FP ' - elif pitch == VARIABLE_PITCH: - info += 'VP ' + """callback function to be executed during EnumFontFamiliesEx + call for each font name. it stores names in global variable + """ + global FONTDB + + lf = logfont.contents + name = lf.lfFaceName.decode('utf-8') + + # detect font type (vector|raster) and format (ttf) + # [ ] use Windows constant TRUETYPE_FONTTYPE + if fonttype & 4: + vector = True + fmt = 'ttf' else: - info += ' ' + vector = False + # [ ] research Windows raster format structure + fmt = 'unknown' + + pitch = lf.lfPitchAndFamily & 0b11 + family = lf.lfPitchAndFamily >> 4 + + # [ ] check FIXED_PITCH, VARIABLE_PITCH and FF_MODERN + # combination + # + # FP T NM 400 CHARSET: 0 DFKai-SB + # FP T NM 400 CHARSET: 136 DFKai-SB + # FP T NM 400 CHARSET: 0 @DFKai-SB + # FP T NM 400 CHARSET: 136 @DFKai-SB + # VP T M 400 CHARSET: 0 OCR A Extended + + monospace = (pitch == FIXED_PITCH) + + charset = lf.lfCharSet + + FONTDB.append(FontEntry(name, vector, fmt, monospace, family)) + + if DEBUG: + info = '' + + if pitch == FIXED_PITCH: + info += 'FP ' + elif pitch == VARIABLE_PITCH: + info += 'VP ' + else: + info += ' ' + + # [ ] check exact fonttype values meaning + info += '%s ' % {0: 'U', 1: 'R', 4: 'T'}[fonttype] + + if monospace: + info += 'M ' + else: + info += 'NM ' + + style = [' '] * 3 + if lf.lfItalic: + style[0] = 'I' + if lf.lfUnderline: + style[1] = 'U' + if lf.lfStrikeOut: + style[2] = 'S' + info += ''.join(style) + + info += ' %s' % lf.lfWeight + + # if pitch == FIXED_PITCH: + if 1: + print('%s CHARSET: %3s %s' % (info, lf.lfCharSet, lf.lfFaceName)) - # [ ] check exact fonttype values meaning - info += '%s ' % {0:'U', 1:'R', 4:'T'}[fonttype] - - if monospace: - info += 'M ' - else: - info += 'NM ' + return 1 # non-0 to continue enumeration - style = [' ']*3 - if lf.lfItalic: - style[0] = 'I' - if lf.lfUnderline: - style[1] = 'U' - if lf.lfStrikeOut: - style[2] = 'S' - info += ''.join(style) - - info += ' %s' % lf.lfWeight - - #if pitch == FIXED_PITCH: - if 1: - print('%s CHARSET: %3s %s' % (info, lf.lfCharSet, lf.lfFaceName)) - return 1 # non-0 to continue enumeration enum_font_names = FONTENUMPROC(_enum_font_names) + # --- /define # --- prepare and call EnumFontFamiliesEx def query(charset=DEFAULT_CHARSET): - """ - Prepare and call EnumFontFamiliesEx. + """ + Prepare and call EnumFontFamiliesEx. - query() - - return tuple with sorted list of all available system fonts - query(charset=ANSI_CHARSET) - - return tuple sorted list of system fonts supporting ANSI charset - - """ - global FONTDB - - # 1. Get device context of the entire screen - hdc = user32.GetDC(None) - - # 2. Call EnumFontFamiliesExA (ANSI version) - - # 2a. Call with empty font name to query all available fonts - # (or fonts for the specified charset) - # - # NOTES: - # - # * there are fonts that don't support ANSI charset - # * for DEFAULT_CHARSET font is passed to callback function as - # many times as charsets it supports - - # [ ] font name should be less than 32 symbols with terminating \0 - # [ ] check double purpose - enumerate all available font names - # - enumerate all available charsets for a single font - # - other params? - - logfont = LOGFONT(0, 0, 0, 0, 0, 0, 0, 0, charset, - 0, 0, 0, 0, b'\0') - FONTDB = [] # clear cached FONTDB for enum_font_names callback - res = gdi32.EnumFontFamiliesExA( - hdc, # handle to device context - ctypes.byref(logfont), - enum_font_names, # pointer to callback function - 0, # lParam - application-supplied data - 0) # dwFlags - reserved = 0 - # res here is the last value returned by callback function + query() + - return tuple with sorted list of all available system fonts + query(charset=ANSI_CHARSET) + - return tuple sorted list of system fonts supporting ANSI charset + + """ + global FONTDB + + # 1. Get device context of the entire screen + hdc = user32.GetDC(None) + + # 2. Call EnumFontFamiliesExA (ANSI version) + + # 2a. Call with empty font name to query all available fonts + # (or fonts for the specified charset) + # + # NOTES: + # + # * there are fonts that don't support ANSI charset + # * for DEFAULT_CHARSET font is passed to callback function as + # many times as charsets it supports + + # [ ] font name should be less than 32 symbols with terminating \0 + # [ ] check double purpose - enumerate all available font names + # - enumerate all available charsets for a single font + # - other params? + + logfont = LOGFONT(0, 0, 0, 0, 0, 0, 0, 0, charset, 0, 0, 0, 0, b'\0') + FONTDB = [] # clear cached FONTDB for enum_font_names callback + res = gdi32.EnumFontFamiliesExA( + hdc, # handle to device context + ctypes.byref(logfont), + enum_font_names, # pointer to callback function + 0, # lParam - application-supplied data + 0) # dwFlags - reserved = 0 + # res here is the last value returned by callback function - # 3. Release DC - user32.ReleaseDC(None, hdc) + # 3. Release DC + user32.ReleaseDC(None, hdc) - return FONTDB + return FONTDB # --- Public API --- def have_font(name, refresh=False): - """ - Return True if font with specified `name` is present. The result - of querying system font names is cached. Set `refresh` parameter - to True to purge cache and reload font information. - """ - if not FONTDB or refresh: - query() - if any(f.name == name for f in FONTDB): - return True - else: - return False + """ + Return True if font with specified `name` is present. The result + of querying system font names is cached. Set `refresh` parameter + to True to purge cache and reload font information. + """ + if not FONTDB or refresh: + query() + if any(f.name == name for f in FONTDB): + return True + else: + return False + def font_list(vector_only=False, monospace_only=False): - """Return list of system installed font names.""" + """Return list of system installed font names.""" - if not FONTDB: - query() + if not FONTDB: + query() - fonts = FONTDB - if vector_only: - fonts = [f for f in fonts if f.vector] - if monospace_only: - fonts = [f for f in fonts if f.monospace] + fonts = FONTDB + if vector_only: + fonts = [f for f in fonts if f.vector] + if monospace_only: + fonts = [f for f in fonts if f.monospace] - return sorted([f.name for f in fonts]) + return sorted([f.name for f in fonts]) + +# TODO: move this into tests/ if __name__ == '__main__': - import sys - if sys.argv[1:] == ['debug']: - DEBUG = True - - if sys.argv[1:] == ['test'] or DEBUG: - print('Running tests..') - # test have_font (Windows) - test_arial = have_font('Arial') - print('Have font "Arial"? %s' % test_arial) - print('Have font "missing-one"? %s' % have_font('missing-one')) - # test cache is not rebuilt - FONTDB = [FontEntry('stub', False, '', False, FF_MODERN)] - assert(have_font('Arial') != test_arial) - # test cache is rebiult - assert(have_font('Arial', refresh=True) == test_arial) - if not DEBUG: - sys.exit() - - if sys.argv[1:] == ['vector']: - fonts = font_list(vector_only=True) - elif sys.argv[1:] == ['mono']: - fonts = font_list(monospace_only=True) - elif sys.argv[1:] == ['vector','mono']: - fonts = font_list(vector_only=True, monospace_only=True) - else: - fonts = font_list() - print('\n'.join(fonts)) + import sys - if DEBUG: - print("Total: %s" % len(font_list())) + if sys.argv[1:] == ['debug']: + DEBUG = True + if sys.argv[1:] == ['test'] or DEBUG: + print('Running tests..') + # test have_font (Windows) + test_arial = have_font('Arial') + print('Have font "Arial"? %s' % test_arial) + print('Have font "missing-one"? %s' % have_font('missing-one')) + # test cache is not rebuilt + FONTDB = [FontEntry('stub', False, '', False, FF_MODERN)] + assert (have_font('Arial') != test_arial) + # test cache is rebiult + assert (have_font('Arial', refresh=True) == test_arial) + if not DEBUG: + sys.exit() + + if sys.argv[1:] == ['vector']: + fonts = font_list(vector_only=True) + elif sys.argv[1:] == ['mono']: + fonts = font_list(monospace_only=True) + elif sys.argv[1:] == ['vector', 'mono']: + fonts = font_list(vector_only=True, monospace_only=True) + else: + fonts = font_list() + print('\n'.join(fonts)) + if DEBUG: + print("Total: %s" % len(font_list())) -#-- CHAPTER 2: WORK WITH FONT DIMENSIONS -- +# -- CHAPTER 2: WORK WITH FONT DIMENSIONS -- # # Essential info about font metrics http://support.microsoft.com/kb/32667 # And about logical units at http://www.winprog.org/tutorial/fonts.html @@ -546,7 +548,7 @@ # x.1 Get pixels per inch using GetDeviceCaps() or ... -#-- CHAPTER 3: LAYERED FONT API -- +# -- CHAPTER 3: LAYERED FONT API -- # # y. Font object with several layers of info diff -Nru pyglet-1.4.10/pyglet/gl/agl.py pyglet-1.5.14/pyglet/gl/agl.py --- pyglet-1.4.10/pyglet/gl/agl.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/agl.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,14 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for /System/Library/Frameworks/AGL.framework/Headers/agl.h + +"""Wrapper for /System/Library/Frameworks/AGL.framework/Headers/agl.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: gengl.py 601 2007-02-04 05:36:59Z Alex.Holkner $' +""" from ctypes import * from pyglet.gl.lib import link_AGL as _link_function diff -Nru pyglet-1.4.10/pyglet/gl/base.py pyglet-1.5.14/pyglet/gl/base.py --- pyglet-1.4.10/pyglet/gl/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/base.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,13 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from builtins import object from pyglet import gl, compat_platform from pyglet.gl import gl_info from pyglet.gl import glu_info -class Config(object): + +class Config: """Graphics configuration. A Config stores the preferences for OpenGL attributes such as the @@ -234,14 +234,14 @@ return True -class ObjectSpace(object): +class ObjectSpace: def __init__(self): # Textures and buffers scheduled for deletion the next time this # object space is active. self._doomed_textures = [] self._doomed_buffers = [] -class Context(object): +class Context: """OpenGL context for drawing. Use `CanvasConfig.create_context` to create a context. diff -Nru pyglet-1.4.10/pyglet/gl/cocoa.py pyglet-1.5.14/pyglet/gl/cocoa.py --- pyglet-1.4.10/pyglet/gl/cocoa.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/cocoa.py 2020-12-31 21:03:32.000000000 +0000 @@ -68,6 +68,10 @@ yosemite: 14.0 -> 14.5 el_capitan: 15.0 -> 15.6 sierra: 16.0 -> 16.6 +high_sierra: 17.0 -> 17.7 +mojave: 18.0 -> 18.2 +catalina: 19.0 -> 19.6 +big_sur: 20.0 -> """ os_x_release = { 'pre-release': (0,1), @@ -85,7 +89,11 @@ 'yosemite': (14,), 'el_capitan': (15,), 'sierra': (16,), - } + 'high_sierra': (17,), + 'mojave': (18,), + 'catalina': (19,), + 'big_sur': (20,) +} def os_x_version(): version = tuple([int(v) for v in platform.release().split('.')]) diff -Nru pyglet-1.4.10/pyglet/gl/glext_arb.py pyglet-1.5.14/pyglet/gl/glext_arb.py --- pyglet-1.4.10/pyglet/gl/glext_arb.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glext_arb.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,14 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for http://oss.sgi.com/projects/ogl-sample/ABI/glext.h + +"""Wrapper for http://oss.sgi.com/projects/ogl-sample/ABI/glext.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" from ctypes import * from pyglet.gl.lib import link_GL as _link_function diff -Nru pyglet-1.4.10/pyglet/gl/glext_nv.py pyglet-1.5.14/pyglet/gl/glext_nv.py --- pyglet-1.4.10/pyglet/gl/glext_nv.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glext_nv.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,14 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for http://developer.download.nvidia.com/opengl/includes/glext.h + +"""Wrapper for http://developer.download.nvidia.com/opengl/includes/glext.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" from ctypes import * from pyglet.gl.lib import link_GL as _link_function diff -Nru pyglet-1.4.10/pyglet/gl/gl_info.py pyglet-1.5.14/pyglet/gl/gl_info.py --- pyglet-1.4.10/pyglet/gl/gl_info.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/gl_info.py 2020-12-23 15:47:40.000000000 +0000 @@ -55,20 +55,15 @@ # ... """ -from builtins import range -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' from ctypes import c_char_p, cast import warnings from pyglet.gl.gl import GL_EXTENSIONS, GL_RENDERER, GL_VENDOR, GL_VERSION, GLint, glGetIntegerv, glGetString -from pyglet.compat import asstr +from pyglet.util import asstr -class GLInfo(object): +class GLInfo: """Information interface for a single GL context. A default instance is created automatically when the first OpenGL context diff -Nru pyglet-1.4.10/pyglet/gl/gl.py pyglet-1.5.14/pyglet/gl/gl.py --- pyglet-1.4.10/pyglet/gl/gl.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/gl.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,11 +32,11 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for /usr/include/GL/gl.h +"""Wrapper for /usr/include/GL/gl.h Generated by tools/gengl.py. Do not modify this file. -''' +""" __docformat__ = 'restructuredtext' __version__ = '$Id$' diff -Nru pyglet-1.4.10/pyglet/gl/glu_info.py pyglet-1.5.14/pyglet/gl/glu_info.py --- pyglet-1.4.10/pyglet/gl/glu_info.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glu_info.py 2020-12-23 15:47:40.000000000 +0000 @@ -56,19 +56,15 @@ Note that GLUInfo only returns meaningful information if a context has been created. """ -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' from ctypes import * import warnings from pyglet.gl.glu import * -from pyglet.compat import asstr +from pyglet.util import asstr -class GLUInfo(object): +class GLUInfo: """Information interface for the GLU library. A default instance is created automatically when the first OpenGL context diff -Nru pyglet-1.4.10/pyglet/gl/glu.py pyglet-1.5.14/pyglet/gl/glu.py --- pyglet-1.4.10/pyglet/gl/glu.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glu.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,18 +32,15 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for /usr/include/GL/glu.h + +"""Wrapper for /usr/include/GL/glu.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" from ctypes import * from pyglet.gl.lib import link_GLU as _link_function -from pyglet.gl.lib import c_ptrdiff_t # BEGIN GENERATED CONTENT (do not edit below this line) diff -Nru pyglet-1.4.10/pyglet/gl/glxext_arb.py pyglet-1.5.14/pyglet/gl/glxext_arb.py --- pyglet-1.4.10/pyglet/gl/glxext_arb.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glxext_arb.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,19 +32,16 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for http://oss.sgi.com/projects/ogl-sample/ABI/glxext.h +"""Wrapper for http://oss.sgi.com/projects/ogl-sample/ABI/glxext.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" import ctypes from ctypes import * from pyglet.gl.lib import link_GLX as _link_function -from pyglet.gl.lib import c_ptrdiff_t + if not hasattr(ctypes, 'c_int64'): # XXX TODO completely wrong, but at least can import. diff -Nru pyglet-1.4.10/pyglet/gl/glxext_mesa.py pyglet-1.5.14/pyglet/gl/glxext_mesa.py --- pyglet-1.4.10/pyglet/gl/glxext_mesa.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glxext_mesa.py 2020-12-15 21:20:32.000000000 +0000 @@ -32,14 +32,11 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''This file is currently hand-coded; I don't have a MESA header file to build -off. -''' -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +"""This file is currently hand-coded; I don't have a MESA header file to build +off. +""" -import ctypes from ctypes import * from pyglet.gl.lib import link_GLX as _link_function diff -Nru pyglet-1.4.10/pyglet/gl/glxext_nv.py pyglet-1.5.14/pyglet/gl/glxext_nv.py --- pyglet-1.4.10/pyglet/gl/glxext_nv.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glxext_nv.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,18 +32,17 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for http://developer.download.nvidia.com/opengl/includes/glxext.h + +"""Wrapper for http://developer.download.nvidia.com/opengl/includes/glxext.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" from ctypes import * from pyglet.gl.lib import link_GLX as _link_function -from pyglet.gl.lib import c_ptrdiff_t + + # BEGIN GENERATED CONTENT (do not edit below this line) # This content is generated by tools/gengl.py. diff -Nru pyglet-1.4.10/pyglet/gl/glx_info.py pyglet-1.5.14/pyglet/gl/glx_info.py --- pyglet-1.4.10/pyglet/gl/glx_info.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glx_info.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,7 +33,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Information about version and extensions of current GLX implementation. +"""Information about version and extensions of current GLX implementation. Usage:: @@ -50,21 +50,19 @@ if info.get_server_vendor() == 'ATI': # ... -''' -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" from ctypes import * from pyglet.gl.glx import * -from pyglet.compat import asstr +from pyglet.util import asstr + class GLXInfoException(Exception): pass -class GLXInfo(object): + +class GLXInfo: def __init__(self, display=None): # Set default display if not set if display and not _glx_info.display: @@ -89,13 +87,13 @@ server = [int(i) for i in server_version.split('.')] client = [int(i) for i in client_version.split('.')] - return (tuple(server) >= (major, minor) and + return (tuple(server) >= (major, minor) and tuple(client) >= (major, minor)) def get_server_vendor(self): self.check_display() return asstr(glXQueryServerString(self.display, 0, GLX_VENDOR)) - + def get_server_version(self): # glXQueryServerString was introduced in GLX 1.1, so we need to use the # 1.0 function here which queries the server implementation for its @@ -105,7 +103,7 @@ minor = c_int() if not glXQueryVersion(self.display, byref(major), byref(minor)): raise GLXInfoException('Could not determine GLX server version') - return '%s.%s'%(major.value, minor.value) + return '%s.%s' % (major.value, minor.value) def get_server_extensions(self): self.check_display() @@ -133,6 +131,7 @@ return False return extension in self.get_extensions() + # Single instance suitable for apps that use only a single display. _glx_info = GLXInfo() diff -Nru pyglet-1.4.10/pyglet/gl/glx.py pyglet-1.5.14/pyglet/gl/glx.py --- pyglet-1.4.10/pyglet/gl/glx.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/glx.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,13 +32,11 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for /usr/include/GL/glx.h -Do not modify generated portions of this file. -''' +"""Wrapper for /usr/include/GL/glx.h -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +Do not modify generated portions of this file. +""" from ctypes import * from pyglet.gl.lib import link_GLX as _link_function diff -Nru pyglet-1.4.10/pyglet/gl/__init__.py pyglet-1.5.14/pyglet/gl/__init__.py --- pyglet-1.4.10/pyglet/gl/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -33,7 +33,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''OpenGL and GLU interface. +"""OpenGL and GLU interface. This package imports all OpenGL, GLU and registered OpenGL extension functions. Functions have identical signatures to their C counterparts. For @@ -90,21 +90,22 @@ The information modules are provided for convenience, and are documented below. -''' -from __future__ import print_function -from __future__ import absolute_import -from builtins import range +""" -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -from pyglet.gl.lib import GLException from pyglet.gl.gl import * from pyglet.gl.glu import * +from pyglet.gl.lib import GLException from pyglet.gl.glext_arb import * from pyglet.gl import gl_info +import pyglet as _pyglet + +from pyglet import compat_platform +from .base import ObjectSpace, CanvasConfig, Context + + import sys as _sys + _is_pyglet_doc_run = hasattr(_sys, "is_pyglet_doc_run") and _sys.is_pyglet_doc_run #: The active OpenGL context. @@ -117,26 +118,14 @@ #: .. versionadded:: 1.1 current_context = None -def get_current_context(): - '''Return the active OpenGL context. - - You can change the current context by calling `Context.set_current`. - - :deprecated: Use `current_context` - - :rtype: `Context` - :return: the context to which OpenGL commands are directed, or None - if there is no selected context. - ''' - return current_context class ContextException(Exception): pass + class ConfigException(Exception): pass -import pyglet as _pyglet if _pyglet.options['debug_texture']: _debug_texture_total = 0 @@ -160,15 +149,20 @@ print('%d (-%d)' % (_debug_texture_total, size)) + _glBindTexture = glBindTexture + + def glBindTexture(target, texture): global _debug_texture _debug_texture = texture return _glBindTexture(target, texture) + _glTexImage2D = glTexImage2D - def glTexImage2D(target, level, internalformat, width, height, border, - format, type, pixels): + + + def glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels): try: _debug_texture_dealloc(_debug_texture) except KeyError: @@ -181,14 +175,16 @@ elif internalformat in (3, GL_RGB): depth = 3 else: - depth = 4 # Pretty crap assumption + depth = 4 # Pretty crap assumption size = (width + 2 * border) * (height + 2 * border) * depth _debug_texture_alloc(_debug_texture, size) - return _glTexImage2D(target, level, internalformat, width, height, - border, format, type, pixels) + return _glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels) + _glDeleteTextures = glDeleteTextures + + def glDeleteTextures(n, textures): if not hasattr(textures, '__len__'): _debug_texture_dealloc(textures.value) @@ -198,6 +194,7 @@ return _glDeleteTextures(n, textures) + def _create_shadow_window(): global _shadow_window @@ -212,8 +209,7 @@ from pyglet import app app.windows.remove(_shadow_window) -from pyglet import compat_platform -from .base import ObjectSpace, CanvasConfig, Context + if _is_pyglet_doc_run: from .base import Config elif compat_platform in ('win32', 'cygwin'): @@ -222,16 +218,12 @@ from .xlib import XlibConfig as Config elif compat_platform == 'darwin': from .cocoa import CocoaConfig as Config -del base # noqa: F821 -# XXX remove + _shadow_window = None -# Import pyglet.window now if it isn't currently being imported (this creates -# the shadow window). -if (not _is_pyglet_doc_run and - 'pyglet.window' not in _sys.modules and - _pyglet.options['shadow_window']): +# Import pyglet.window now if it isn't currently being imported (this creates the shadow window). +if not _is_pyglet_doc_run and 'pyglet.window' not in _sys.modules and _pyglet.options['shadow_window']: # trickery is for circular import _pyglet.gl = _sys.modules[__name__] import pyglet.window diff -Nru pyglet-1.4.10/pyglet/gl/lib_agl.py pyglet-1.5.14/pyglet/gl/lib_agl.py --- pyglet-1.4.10/pyglet/gl/lib_agl.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/lib_agl.py 2020-12-31 21:03:32.000000000 +0000 @@ -33,12 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - from ctypes import * import pyglet.lib @@ -46,10 +40,9 @@ __all__ = ['link_GL', 'link_GLU', 'link_AGL'] -gl_lib = pyglet.lib.load_library( - framework='/System/Library/Frameworks/OpenGL.framework') -agl_lib = pyglet.lib.load_library( - framework='/System/Library/Frameworks/AGL.framework') +gl_lib = pyglet.lib.load_library(framework='OpenGL') +agl_lib = pyglet.lib.load_library(framework='AGL') + def link_GL(name, restype, argtypes, requires=None, suggestions=None): try: @@ -61,8 +54,10 @@ except AttributeError: return missing_function(name, requires, suggestions) + link_GLU = link_GL + def link_AGL(name, restype, argtypes, requires=None, suggestions=None): try: func = getattr(agl_lib, name) @@ -72,4 +67,3 @@ return func except AttributeError: return missing_function(name, requires, suggestions) - diff -Nru pyglet-1.4.10/pyglet/gl/lib_glx.py pyglet-1.5.14/pyglet/gl/lib_glx.py --- pyglet-1.4.10/pyglet/gl/lib_glx.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/lib_glx.py 2020-12-23 15:47:40.000000000 +0000 @@ -33,26 +33,19 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - from ctypes import * import pyglet.lib from pyglet.gl.lib import missing_function, decorate_function -from pyglet.compat import asbytes +from pyglet.util import asbytes __all__ = ['link_GL', 'link_GLU', 'link_GLX'] gl_lib = pyglet.lib.load_library('GL') glu_lib = pyglet.lib.load_library('GLU') -# Look for glXGetProcAddressARB extension, use it as fallback (for -# ATI fglrx and DRI drivers). +# Look for glXGetProcAddressARB extension, use it as fallback (for ATI fglrx and DRI drivers). try: glXGetProcAddressARB = getattr(gl_lib, 'glXGetProcAddressARB') glXGetProcAddressARB.restype = POINTER(CFUNCTYPE(None)) @@ -60,7 +53,8 @@ _have_getprocaddress = True except AttributeError: _have_getprocaddress = False - + + def link_GL(name, restype, argtypes, requires=None, suggestions=None): try: func = getattr(gl_lib, name) @@ -81,8 +75,10 @@ return missing_function(name, requires, suggestions) + link_GLX = link_GL + def link_GLU(name, restype, argtypes, requires=None, suggestions=None): try: func = getattr(glu_lib, name) @@ -92,4 +88,3 @@ return func except AttributeError: return missing_function(name, requires, suggestions) - diff -Nru pyglet-1.4.10/pyglet/gl/lib.py pyglet-1.5.14/pyglet/gl/lib.py --- pyglet-1.4.10/pyglet/gl/lib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/lib.py 2020-12-23 15:47:40.000000000 +0000 @@ -33,23 +33,18 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - import ctypes import pyglet -__all__ = ['link_GL', 'link_GLU', 'link_AGL', 'link_GLX', 'link_WGL'] +__all__ = ['link_GL', 'link_GLU', 'link_AGL', 'link_GLX', 'link_WGL', + 'GLException', 'missing_function', 'decorate_function'] _debug_gl = pyglet.options['debug_gl'] _debug_gl_trace = pyglet.options['debug_gl_trace'] _debug_gl_trace_args = pyglet.options['debug_gl_trace_args'] + class MissingFunctionException(Exception): def __init__(self, name, requires=None, suggestions=None): msg = '%s is not exported by the available OpenGL driver.' % name @@ -59,11 +54,14 @@ msg += ' Consider alternative(s) %s.' % ', '.join(suggestions) Exception.__init__(self, msg) + def missing_function(name, requires=None, suggestions=None): def MissingFunction(*args, **kwargs): raise MissingFunctionException(name, requires, suggestions) + return MissingFunction + _int_types = (ctypes.c_int16, ctypes.c_int32) if hasattr(ctypes, 'c_int64'): # Some builds of ctypes apparently do not have c_int64 @@ -74,15 +72,18 @@ if ctypes.sizeof(t) == ctypes.sizeof(ctypes.c_size_t): c_ptrdiff_t = t + class c_void(ctypes.Structure): # c_void_p is a buggy return type, converting to int, so # POINTER(None) == c_void_p is actually written as # POINTER(c_void), so it can be treated as a real pointer. _fields_ = [('dummy', ctypes.c_int)] + class GLException(Exception): pass + def errcheck(result, func, arguments): if _debug_gl_trace: try: @@ -106,6 +107,7 @@ raise GLException(msg) return result + def errcheck_glbegin(result, func, arguments): from pyglet import gl context = gl.current_context @@ -114,6 +116,7 @@ context._gl_begin = True return result + def errcheck_glend(result, func, arguments): from pyglet import gl context = gl.current_context @@ -122,6 +125,7 @@ context._gl_begin = False return errcheck(result, func, arguments) + def decorate_function(func, name): if _debug_gl: if name == 'glBegin': @@ -129,9 +133,10 @@ elif name == 'glEnd': func.errcheck = errcheck_glend elif name not in ('glGetError', 'gluErrorString') and \ - name[:3] not in ('glX', 'agl', 'wgl'): + name[:3] not in ('glX', 'agl', 'wgl'): func.errcheck = errcheck + link_AGL = None link_GLX = None link_WGL = None @@ -142,4 +147,3 @@ from pyglet.gl.lib_agl import link_GL, link_GLU, link_AGL else: from pyglet.gl.lib_glx import link_GL, link_GLU, link_GLX - diff -Nru pyglet-1.4.10/pyglet/gl/lib_wgl.py pyglet-1.5.14/pyglet/gl/lib_wgl.py --- pyglet-1.4.10/pyglet/gl/lib_wgl.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/lib_wgl.py 2020-12-23 15:47:40.000000000 +0000 @@ -33,19 +33,12 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: lib_glx.py 597 2007-02-03 16:13:07Z Alex.Holkner $' - import ctypes from ctypes import * import pyglet from pyglet.gl.lib import missing_function, decorate_function -from pyglet.compat import asbytes +from pyglet.util import asbytes __all__ = ['link_GL', 'link_GLU', 'link_WGL'] @@ -57,6 +50,7 @@ if _debug_trace: from pyglet.lib import _TraceLibrary + gl_lib = _TraceLibrary(gl_lib) glu_lib = _TraceLibrary(glu_lib) wgl_lib = _TraceLibrary(wgl_lib) @@ -69,16 +63,18 @@ except AttributeError: _have_get_proc_address = False -class_slots = ['name', 'requires', 'suggestions', 'ftype','func'] +class_slots = ['name', 'requires', 'suggestions', 'ftype', 'func'] + def makeWGLFunction(func): - class WGLFunction(object): + class WGLFunction: __slots__ = class_slots __call__ = func - + return WGLFunction -class WGLFunctionProxy(object): + +class WGLFunctionProxy: __slots__ = class_slots def __init__(self, name, ftype, requires, suggestions): @@ -103,9 +99,10 @@ self.name, self.requires, self.suggestions) self.__class__ = makeWGLFunction(self.func) - + return self.func(*args, **kwargs) + def link_GL(name, restype, argtypes, requires=None, suggestions=None): try: func = getattr(gl_lib, name) @@ -134,6 +131,7 @@ return missing_function(name, requires, suggestions) + def link_GLU(name, restype, argtypes, requires=None, suggestions=None): try: func = getattr(glu_lib, name) @@ -162,4 +160,5 @@ return missing_function(name, requires, suggestions) + link_WGL = link_GL diff -Nru pyglet-1.4.10/pyglet/gl/wglext_arb.py pyglet-1.5.14/pyglet/gl/wglext_arb.py --- pyglet-1.4.10/pyglet/gl/wglext_arb.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/wglext_arb.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,19 +32,16 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for http://oss.sgi.com/projects/ogl-sample/ABI/wglext.h + +"""Wrapper for http://oss.sgi.com/projects/ogl-sample/ABI/wglext.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: gengl.py 601 2007-02-04 05:36:59Z Alex.Holkner $' +""" from ctypes import * from pyglet.gl.lib import link_WGL as _link_function -from pyglet.gl.lib import c_ptrdiff_t, c_void - +from pyglet.gl.lib import c_void # BEGIN GENERATED CONTENT (do not edit below this line) diff -Nru pyglet-1.4.10/pyglet/gl/wglext_nv.py pyglet-1.5.14/pyglet/gl/wglext_nv.py --- pyglet-1.4.10/pyglet/gl/wglext_nv.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/wglext_nv.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,18 +32,17 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for http://developer.download.nvidia.com/opengl/includes/wglext.h + +"""Wrapper for http://developer.download.nvidia.com/opengl/includes/wglext.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: gengl.py 601 2007-02-04 05:36:59Z Alex.Holkner $' +""" from ctypes import * from pyglet.gl.lib import link_WGL as _link_function -from pyglet.gl.lib import c_ptrdiff_t, c_void +from pyglet.gl.lib import c_void + # BEGIN GENERATED CONTENT (do not edit below this line) diff -Nru pyglet-1.4.10/pyglet/gl/wgl_info.py pyglet-1.5.14/pyglet/gl/wgl_info.py --- pyglet-1.4.10/pyglet/gl/wgl_info.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/wgl_info.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,13 +33,9 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Cached information about version and extensions of current WGL +"""Cached information about version and extensions of current WGL implementation. -''' -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: glx_info.py 615 2007-02-07 13:17:05Z Alex.Holkner $' +""" from ctypes import * import warnings @@ -49,12 +45,14 @@ from pyglet.gl import gl_info from pyglet.gl.wgl import * from pyglet.gl.wglext_arb import * -from pyglet.compat import asstr +from pyglet.util import asstr + class WGLInfoException(Exception): pass -class WGLInfo(object): + +class WGLInfo: def get_extensions(self): if not gl_info.have_context(): warnings.warn("Can't query WGL until a context is created.") @@ -68,6 +66,7 @@ def have_extension(self, extension): return extension in self.get_extensions() + _wgl_info = WGLInfo() get_extensions = _wgl_info.get_extensions diff -Nru pyglet-1.4.10/pyglet/gl/wgl.py pyglet-1.5.14/pyglet/gl/wgl.py --- pyglet-1.4.10/pyglet/gl/wgl.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/wgl.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,18 +32,16 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for C:\cygwin\home\Alex\pyglet\tools\wgl.h + +"""Wrapper for C:\cygwin\home\Alex\pyglet\tools\wgl.h Generated by tools/gengl.py. Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: gengl.py 601 2007-02-04 05:36:59Z Alex.Holkner $' +""" from ctypes import * from pyglet.gl.lib import link_WGL as _link_function -from pyglet.gl.lib import c_ptrdiff_t + if not _link_function: raise ImportError('opengl32.dll is not available.') diff -Nru pyglet-1.4.10/pyglet/gl/win32.py pyglet-1.5.14/pyglet/gl/win32.py --- pyglet-1.4.10/pyglet/gl/win32.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/win32.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,8 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import -from builtins import zip from pyglet.canvas.win32 import Win32Canvas from .base import Config, CanvasConfig, Context @@ -188,6 +186,7 @@ 'accum_blue_size': wglext_arb.WGL_ACCUM_BLUE_BITS_ARB, 'accum_alpha_size': wglext_arb.WGL_ACCUM_ALPHA_BITS_ARB, } + def __init__(self, canvas, pf, config): super(Win32CanvasConfigARB, self).__init__(canvas, config) self._pf = pf @@ -239,7 +238,7 @@ raise gl.ContextException('Unable to share contexts.') def set_current(self): - if self._context is not None: + if self._context is not None and self != gl.current_context: wgl.wglMakeCurrent(self.canvas.hdc, self._context) super(Win32Context, self).set_current() diff -Nru pyglet-1.4.10/pyglet/gl/xlib.py pyglet-1.5.14/pyglet/gl/xlib.py --- pyglet-1.4.10/pyglet/gl/xlib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/gl/xlib.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import import warnings from ctypes import * @@ -60,10 +59,7 @@ if have_13: config_class = XlibCanvasConfig13 else: - if 'ATI' in info.get_client_vendor(): - config_class = XlibCanvasConfig10ATI - else: - config_class = XlibCanvasConfig10 + config_class = XlibCanvasConfig10 # Construct array of attributes attrs = [] @@ -77,21 +73,16 @@ else: attrs.extend([glx.GLX_RGBA, True]) - if len(attrs): - attrs.extend([0, 0]) - attrib_list = (c_int * len(attrs))(*attrs) - else: - attrib_list = None + attrs.extend([0, 0]) # attrib_list must be null terminated + attrib_list = (c_int * len(attrs))(*attrs) if have_13: elements = c_int() - configs = glx.glXChooseFBConfig(x_display, x_screen, - attrib_list, byref(elements)) + configs = glx.glXChooseFBConfig(x_display, x_screen, attrib_list, byref(elements)) if not configs: return [] - configs = cast(configs, - POINTER(glx.GLXFBConfig * elements.value)).contents + configs = cast(configs, POINTER(glx.GLXFBConfig * elements.value)).contents result = [config_class(canvas, info, c, self) for c in configs] @@ -173,12 +164,6 @@ return XlibContext10(self, share) -class XlibCanvasConfig10ATI(XlibCanvasConfig10): - attribute_ids = BaseXlibCanvasConfig.attribute_ids.copy() - del attribute_ids['stereo'] - stereo = False - - class XlibCanvasConfig13(BaseXlibCanvasConfig): attribute_ids = BaseXlibCanvasConfig.attribute_ids.copy() attribute_ids.update({ @@ -281,8 +266,7 @@ def _create_glx_context(self, share): if self.config.requires_gl_3(): raise gl.ContextException( - 'Require GLX_ARB_create_context extension to create ' + - 'OpenGL 3 contexts.') + 'Require GLX_ARB_create_context extension to create OpenGL 3 contexts.') if share: share_context = share.glx_context @@ -298,8 +282,7 @@ self.set_current() def set_current(self): - glx.glXMakeCurrent(self.x_display, self.canvas.x_window, - self.glx_context) + glx.glXMakeCurrent(self.x_display, self.canvas.x_window, self.glx_context) super(XlibContext10, self).set_current() def detach(self): diff -Nru pyglet-1.4.10/pyglet/graphics/allocation.py pyglet-1.5.14/pyglet/graphics/allocation.py --- pyglet-1.4.10/pyglet/graphics/allocation.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/graphics/allocation.py 2020-11-10 10:36:26.000000000 +0000 @@ -46,11 +46,6 @@ The allocator maintains references to free space only; it is the caller's responsibility to maintain the allocated regions. """ -from __future__ import print_function -from __future__ import division -from builtins import str -from builtins import zip -from builtins import object # Common cases: # -regions will be the same size (instances of same object, e.g. sprites) @@ -89,7 +84,7 @@ self.requested_capacity = requested_capacity -class Allocator(object): +class Allocator: """Buffer space allocation implementation.""" __slots__ = 'capacity', 'starts', 'sizes' diff -Nru pyglet-1.4.10/pyglet/graphics/__init__.py pyglet-1.5.14/pyglet/graphics/__init__.py --- pyglet-1.4.10/pyglet/graphics/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/graphics/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,9 +32,8 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ -'''Low-level graphics rendering. +"""Low-level graphics rendering. This module provides an efficient low-level abstraction over OpenGL. It gives very good performance for rendering OpenGL primitives; far better than the @@ -42,7 +41,7 @@ display lists in many cases. The module is used internally by other areas of pyglet. -See the :ref:`programming-guide-graphics` for details on how to use this graphics API. +See the :ref:`guide_graphics` for details on how to use this graphics API. Batches and groups ================== @@ -157,15 +156,10 @@ video drivers, and requires indexed vertex lists. .. versionadded:: 1.1 -''' -from __future__ import print_function -from builtins import zip -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' +""" import ctypes +import weakref import pyglet from pyglet.gl import * @@ -174,8 +168,9 @@ _debug_graphics_batch = pyglet.options['debug_graphics_batch'] + def draw(size, mode, *data): - '''Draw a primitive immediately. + """Draw a primitive immediately. :Parameters: `size` : int @@ -187,16 +182,14 @@ Attribute formats and data. See the module summary for details. - ''' + """ glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) buffers = [] - for format, array in data: - attribute = vertexattribute.create_attribute(format) - assert size == len(array) // attribute.count, \ - 'Data for %s is incorrect length' % format - buffer = vertexbuffer.create_mappable_buffer( - size * attribute.stride, vbo=False) + for fmt, array in data: + attribute = vertexattribute.create_attribute(fmt) + assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt + buffer = vertexbuffer.create_mappable_buffer(size * attribute.stride, vbo=False) attribute.set_region(buffer, 0, size, array) attribute.enable() @@ -205,11 +198,12 @@ glDrawArrays(mode, 0, size) glFlush() - + glPopClientAttrib() + def draw_indexed(size, mode, indices, *data): - '''Draw a primitive with indexed vertices immediately. + """Draw a primitive with indexed vertices immediately. :Parameters: `size` : int @@ -221,16 +215,14 @@ `data` : data items Attribute formats and data. See the module summary for details. - ''' + """ glPushClientAttrib(GL_CLIENT_VERTEX_ARRAY_BIT) buffers = [] - for format, array in data: - attribute = vertexattribute.create_attribute(format) - assert size == len(array) // attribute.count, \ - 'Data for %s is incorrect length' % format - buffer = vertexbuffer.create_mappable_buffer( - size * attribute.stride, vbo=False) + for fmt, array in data: + attribute = vertexattribute.create_attribute(fmt) + assert size == len(array) // attribute.count, 'Data for %s is incorrect length' % fmt + buffer = vertexbuffer.create_mappable_buffer(size * attribute.stride, vbo=False) attribute.set_region(buffer, 0, size, array) attribute.enable() @@ -250,24 +242,26 @@ index_array = (index_c_type * len(indices))(*indices) glDrawElements(mode, len(indices), index_type, index_array) glFlush() - + glPopClientAttrib() + def _parse_data(data): - '''Given a list of data items, returns (formats, initial_arrays).''' + """Given a list of data items, returns (formats, initial_arrays).""" assert data, 'No attribute formats given' # Return tuple (formats, initial_arrays). formats = [] initial_arrays = [] - for i, format in enumerate(data): - if isinstance(format, tuple): - format, array = format + for i, fmt in enumerate(data): + if isinstance(fmt, tuple): + fmt, array = fmt initial_arrays.append((i, array)) - formats.append(format) + formats.append(fmt) formats = tuple(formats) return formats, initial_arrays + def _get_default_batch(): shared_object_space = gl.current_context.object_space try: @@ -276,8 +270,9 @@ shared_object_space.pyglet_graphics_default_batch = Batch() return shared_object_space.pyglet_graphics_default_batch + def vertex_list(count, *data): - '''Create a :py:class:`~pyglet.graphics.vertexdomain.VertexList` not associated with a batch, group or mode. + """Create a :py:class:`~pyglet.graphics.vertexdomain.VertexList` not associated with a batch, group or mode. :Parameters: `count` : int @@ -287,13 +282,14 @@ module summary for details. :rtype: :py:class:`~pyglet.graphics.vertexdomain.VertexList` - ''' + """ # Note that mode=0 because the default batch is never drawn: vertex lists # returned from this function are drawn directly by the app. return _get_default_batch().add(count, 0, None, *data) + def vertex_list_indexed(count, indices, *data): - '''Create an `IndexedVertexList` not associated with a batch, group or mode. + """Create an `IndexedVertexList` not associated with a batch, group or mode. :Parameters: `count` : int @@ -305,13 +301,14 @@ module summary for details. :rtype: `IndexedVertexList` - ''' + """ # Note that mode=0 because the default batch is never drawn: vertex lists # returned from this function are drawn directly by the app. return _get_default_batch().add_indexed(count, 0, None, indices, *data) -class Batch(object): - '''Manage a collection of vertex lists for batched rendering. + +class Batch: + """Manage a collection of vertex lists for batched rendering. Vertex lists are added to a :py:class:`~pyglet.graphics.Batch` using the `add` and `add_indexed` methods. An optional group can be specified along with the vertex list, @@ -320,9 +317,10 @@ sent to the graphics card in a single operation. Call `VertexList.delete` to remove a vertex list from the batch. - ''' + """ + def __init__(self): - '''Create a graphics batch.''' + """Create a graphics batch.""" # Mapping to find domain. # group -> (attributes, mode, indexed) -> domain self.group_map = {} @@ -337,17 +335,17 @@ self._draw_list_dirty = False def invalidate(self): - '''Force the batch to update the draw list. + """Force the batch to update the draw list. This method can be used to force the batch to re-compute the draw list when the ordering of groups has changed. .. versionadded:: 1.2 - ''' + """ self._draw_list_dirty = True def add(self, count, mode, group, *data): - '''Add a vertex list to the batch. + """Add a vertex list to the batch. :Parameters: `count` : int @@ -363,10 +361,10 @@ the module summary for details. :rtype: :py:class:`~pyglet.graphics.vertexdomain.VertexList` - ''' + """ formats, initial_arrays = _parse_data(data) domain = self._get_domain(False, mode, group, formats) - + # Create vertex list and initialize vlist = domain.create(count) for i, array in initial_arrays: @@ -375,7 +373,7 @@ return vlist def add_indexed(self, count, mode, group, indices, *data): - '''Add an indexed vertex list to the batch. + """Add an indexed vertex list to the batch. :Parameters: `count` : int @@ -393,10 +391,10 @@ the module summary for details. :rtype: `IndexedVertexList` - ''' + """ formats, initial_arrays = _parse_data(data) domain = self._get_domain(True, mode, group, formats) - + # Create vertex list and initialize vlist = domain.create(count, len(indices)) start = vlist.start @@ -404,10 +402,10 @@ for i, array in initial_arrays: vlist._set_attribute_data(i, array) - return vlist + return vlist def migrate(self, vertex_list, mode, group, batch): - '''Migrate a vertex list to another batch and/or group. + """Migrate a vertex list to another batch and/or group. `vertex_list` and `mode` together identify the vertex list to migrate. `group` and `batch` are new owners of the vertex list after migration. @@ -428,7 +426,7 @@ `batch` : `~pyglet.graphics.Batch` The batch to migrate to (or the current batch). - ''' + """ formats = vertex_list.domain.__formats if isinstance(vertex_list, vertexdomain.IndexedVertexList): domain = batch._get_domain(True, mode, group, formats) @@ -439,7 +437,7 @@ def _get_domain(self, indexed, mode, group, formats): if group is None: group = null_group - + # Batch group if group not in self.group_map: self._add_group(group) @@ -458,7 +456,7 @@ domain = vertexdomain.create_domain(*formats) domain.__formats = formats domain_map[key] = domain - self._draw_list_dirty = True + self._draw_list_dirty = True return domain @@ -472,12 +470,14 @@ if group.parent not in self.group_children: self.group_children[group.parent] = [] self.group_children[group.parent].append(group) + + group._assigned_batches.add(self) self._draw_list_dirty = True def _update_draw_list(self): - '''Visit group tree in preorder and create a list of bound methods + """Visit group tree in preorder and create a list of bound methods to call. - ''' + """ def visit(group): draw_list = [] @@ -497,13 +497,15 @@ if children: children.sort() for child in list(children): - draw_list.extend(visit(child)) + if child.visible: + draw_list.extend(visit(child)) if children or domain_map: return [group.set_state] + draw_list + [group.unset_state] else: # Remove unused group from batch del self.group_map[group] + group._assigned_batches.remove(self) if group.parent: self.group_children[group.parent].remove(group) try: @@ -514,13 +516,15 @@ self.top_groups.remove(group) except ValueError: pass + return [] self._draw_list = [] self.top_groups.sort() for group in list(self.top_groups): - self._draw_list.extend(visit(group)) + if group.visible: + self._draw_list.extend(visit(group)) self._draw_list_dirty = False @@ -538,8 +542,7 @@ for key, attribute in domain.attribute_names.items(): print(indent, ' ', end=' ') try: - region = attribute.get_region(attribute.buffer, - start, size) + region = attribute.get_region(attribute.buffer, start, size) print(key, region.array[:]) except: print(key, '(unmappable)') @@ -550,10 +553,10 @@ print('Draw list for %r:' % self) for group in self.top_groups: dump(group) - + def draw(self): - '''Draw the batch. - ''' + """Draw the batch. + """ if self._draw_list_dirty: self._update_draw_list() @@ -561,7 +564,7 @@ func() def draw_subset(self, vertex_lists): - '''Draw only some vertex lists in the batch. + """Draw only some vertex lists in the batch. The use of this method is highly discouraged, as it is quite inefficient. Usually an application can be redesigned so that batches @@ -574,7 +577,8 @@ `vertex_lists` : sequence of `VertexList` or `IndexedVertexList` Vertex lists to draw. - ''' + """ + # Horrendously inefficient. def visit(group): group.set_state() @@ -591,89 +595,117 @@ if children: children.sort() for child in children: - visit(child) + if child.visible: + visit(child) group.unset_state() self.top_groups.sort() for group in self.top_groups: - visit(group) + if group.visible: + visit(group) + -class Group(object): - '''Group of common OpenGL state. +class Group: + """Group of common OpenGL state. Before a vertex list is rendered, its group's OpenGL state is set; as are that state's ancestors' states. This can be defined arbitrarily on subclasses; the default state change has no effect, and groups vertex lists only in the order in which they are drawn. - ''' + """ + def __init__(self, parent=None): - '''Create a group. + """Create a group. :Parameters: `parent` : `~pyglet.graphics.Group` Group to contain this group; its state will be set before this state's. - - ''' + `visible` : bool + Determines whether this group is visible in any of the batches it is assigned to. + `batches` : list + Read Only. A list of which batches this group is a part of. + """ self.parent = parent - + self._visible = True + self._assigned_batches = weakref.WeakSet() + + @property + def visible(self): + return self._visible + + @visible.setter + def visible(self, value): + self._visible = value + + for batch in self._assigned_batches: + batch.invalidate() + + @property + def batches(self): + return [batch for batch in self._assigned_batches] + def __lt__(self, other): return hash(self) < hash(other) def set_state(self): - '''Apply the OpenGL state change. + """Apply the OpenGL state change. - The default implementation does nothing.''' + The default implementation does nothing.""" pass def unset_state(self): - '''Repeal the OpenGL state change. + """Repeal the OpenGL state change. - The default implementation does nothing.''' + The default implementation does nothing.""" pass def set_state_recursive(self): - '''Set this group and its ancestry. + """Set this group and its ancestry. Call this method if you are using a group in isolation: the parent groups will be called in top-down order, with this class's `set` being called last. - ''' + """ if self.parent: self.parent.set_state_recursive() self.set_state() def unset_state_recursive(self): - '''Unset this group and its ancestry. + """Unset this group and its ancestry. The inverse of `set_state_recursive`. - ''' + """ self.unset_state() if self.parent: self.parent.unset_state_recursive() + class NullGroup(Group): - '''The default group class used when ``None`` is given to a batch. + """The default group class used when ``None`` is given to a batch. This implementation has no effect. - ''' + """ pass + #: The default group. #: #: :type: :py:class:`~pyglet.graphics.Group` null_group = NullGroup() + class TextureGroup(Group): - '''A group that enables and binds a texture. + """A group that enables and binds a texture. Texture groups are equal if their textures' targets and names are equal. - ''' + """ + # Don't use this, create your own group classes that are more specific. # This is just an example. def __init__(self, texture, parent=None): - '''Create a texture group. + """Create a texture group. :Parameters: `texture` : `~pyglet.image.Texture` @@ -681,7 +713,7 @@ `parent` : `~pyglet.graphics.Group` Parent group. - ''' + """ super(TextureGroup, self).__init__(parent) self.texture = texture @@ -697,20 +729,22 @@ def __eq__(self, other): return (self.__class__ is other.__class__ and - self.texture.target == other.texture.target and - self.texture.id == other.texture.id and - self.parent == other.parent) + self.texture.target == other.texture.target and + self.texture.id == other.texture.id and + self.parent == other.parent) def __repr__(self): return '%s(id=%d)' % (self.__class__.__name__, self.texture.id) + class OrderedGroup(Group): - '''A group with partial order. + """A group with partial order. Ordered groups with a common parent are rendered in ascending order of their ``order`` field. This is a useful way to render multiple layers of a scene within a single batch. - ''' + """ + # This can be useful as a top-level group, or as a superclass for other # groups that need to be ordered. # @@ -718,7 +752,7 @@ # known order even if they don't know about each other or share any known # group. def __init__(self, order, parent=None): - '''Create an ordered group. + """Create an ordered group. :Parameters: `order` : int @@ -726,7 +760,7 @@ `parent` : `~pyglet.graphics.Group` Parent of this group. - ''' + """ super(OrderedGroup, self).__init__(parent) self.order = order @@ -737,8 +771,8 @@ def __eq__(self, other): return (self.__class__ is other.__class__ and - self.order == other.order and - self.parent == other.parent) + self.order == other.order and + self.parent == other.parent) def __hash__(self): return hash((self.order, self.parent)) diff -Nru pyglet-1.4.10/pyglet/graphics/vertexattribute.py pyglet-1.5.14/pyglet/graphics/vertexattribute.py --- pyglet-1.4.10/pyglet/graphics/vertexattribute.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/graphics/vertexattribute.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,9 +32,8 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ -'''Access byte arrays as arrays of vertex attributes. +"""Access byte arrays as arrays of vertex attributes. Use :py:func:`create_attribute` to create an attribute accessor given a simple format string. Alternatively, the classes may be constructed directly. @@ -130,14 +129,10 @@ ``3t2f`` 2-float texture coordinate for texture unit 3. -''' -from builtins import object +""" -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import ctypes import re +import ctypes from pyglet.gl import * from pyglet.graphics import vertexbuffer @@ -164,22 +159,24 @@ 'd': GL_DOUBLE, } -_attribute_format_re = re.compile(r''' +_attribute_format_re = re.compile(r""" (?P [cefnstv] | (?P[0-9]+) g (?Pn?) | (?P[0-9]+) t) (?P[1234]) (?P[bBsSiIfd]) -''', re.VERBOSE) +""", re.VERBOSE) _attribute_cache = {} + def _align(v, align): return ((v - 1) & ~(align - 1)) + align + def interleave_attributes(attributes): - '''Interleave attribute offsets. + """Interleave attribute offsets. Adjusts the offsets and strides of the given attributes so that they are interleaved. Alignment constraints are respected. @@ -188,11 +185,11 @@ `attributes` : sequence of `AbstractAttribute` Attributes to interleave in-place. - ''' + """ stride = 0 max_size = 0 for attribute in attributes: - stride = _align(stride, attribute.align) + stride = _align(stride, attribute.align) attribute.offset = stride stride += attribute.size max_size = max(max_size, attribute.size) @@ -200,8 +197,9 @@ for attribute in attributes: attribute.stride = stride + def serialize_attributes(count, attributes): - '''Serialize attribute offsets. + """Serialize attribute offsets. Adjust the offsets of the given attributes so that they are packed serially against each other for `count` vertices. @@ -212,15 +210,16 @@ `attributes` : sequence of `AbstractAttribute` Attributes to serialize in-place. - ''' + """ offset = 0 for attribute in attributes: offset = _align(offset, attribute.align) attribute.offset = offset offset += count * attribute.stride + def create_attribute(format): - '''Create a vertex attribute description from a format string. + """Create a vertex attribute description from a format string. The initial stride and offset of the attribute will be 0. @@ -229,7 +228,7 @@ Attribute format string. See the module summary for details. :rtype: `AbstractAttribute` - ''' + """ try: cls, args = _attribute_cache[format] return cls(*args) @@ -259,18 +258,19 @@ args = (gl_type,) else: args = (count, gl_type) - + _attribute_cache[format] = attr_class, args return attr_class(*args) -class AbstractAttribute(object): - '''Abstract accessor for an attribute in a mapped buffer. - ''' - + +class AbstractAttribute: + """Abstract accessor for an attribute in a mapped buffer. + """ + _fixed_count = None - + def __init__(self, count, gl_type): - '''Create the attribute accessor. + """Create the attribute accessor. :Parameters: `count` : int @@ -278,7 +278,7 @@ `gl_type` : int OpenGL type enumerant; for example, ``GL_FLOAT`` - ''' + """ assert count in (1, 2, 3, 4), 'Component count out of range' self.gl_type = gl_type self.c_type = _c_types[gl_type] @@ -289,11 +289,11 @@ self.offset = 0 def enable(self): - '''Enable the attribute using ``glEnableClientState``.''' + """Enable the attribute using ``glEnableClientState``.""" raise NotImplementedError('abstract') def set_pointer(self, offset): - '''Setup this attribute to point to the currently bound buffer at + """Setup this attribute to point to the currently bound buffer at the given offset. ``offset`` should be based on the currently bound buffer's ``ptr`` @@ -304,11 +304,11 @@ Pointer offset to the currently bound buffer for this attribute. - ''' + """ raise NotImplementedError('abstract') def get_region(self, buffer, start, count): - '''Map a buffer region using this attribute as an accessor. + """Map a buffer region using this attribute as an accessor. The returned region can be modified as if the buffer was a contiguous array of this attribute (though it may actually be interleaved or @@ -328,7 +328,7 @@ Number of vertices to map :rtype: `AbstractBufferRegion` - ''' + """ byte_start = self.stride * start byte_size = self.stride * count array_count = self.count * count @@ -349,7 +349,7 @@ region, array_count, self.count, elem_stride) def set_region(self, buffer, start, count, data): - '''Set the data over a region of the buffer. + """Set the data over a region of the buffer. :Parameters: `buffer` : AbstractMappable` @@ -361,7 +361,7 @@ `data` : sequence Sequence of data components. - ''' + """ if self.stride == self.size: # non-interleaved byte_start = self.stride * start @@ -374,28 +374,30 @@ region = self.get_region(buffer, start, count) region[:] = data + class ColorAttribute(AbstractAttribute): - '''Color vertex attribute.''' + """Color vertex attribute.""" plural = 'colors' - + def __init__(self, count, gl_type): assert count in (3, 4), 'Color attributes must have count of 3 or 4' super(ColorAttribute, self).__init__(count, gl_type) def enable(self): glEnableClientState(GL_COLOR_ARRAY) - + def set_pointer(self, pointer): glColorPointer(self.count, self.gl_type, self.stride, self.offset + pointer) + class EdgeFlagAttribute(AbstractAttribute): - '''Edge flag attribute.''' + """Edge flag attribute.""" plural = 'edge_flags' _fixed_count = 1 - + def __init__(self, gl_type): assert gl_type in (GL_BYTE, GL_UNSIGNED_BYTE, GL_BOOL), \ 'Edge flag attribute must have boolean type' @@ -403,27 +405,29 @@ def enable(self): glEnableClientState(GL_EDGE_FLAG_ARRAY) - + def set_pointer(self, pointer): glEdgeFlagPointer(self.stride, self.offset + pointer) + class FogCoordAttribute(AbstractAttribute): - '''Fog coordinate attribute.''' + """Fog coordinate attribute.""" plural = 'fog_coords' - + def __init__(self, count, gl_type): super(FogCoordAttribute, self).__init__(count, gl_type) def enable(self): glEnableClientState(GL_FOG_COORD_ARRAY) - + def set_pointer(self, pointer): glFogCoordPointer(self.count, self.gl_type, self.stride, self.offset + pointer) + class NormalAttribute(AbstractAttribute): - '''Normal vector attribute.''' + """Normal vector attribute.""" plural = 'normals' _fixed_count = 3 @@ -435,12 +439,13 @@ def enable(self): glEnableClientState(GL_NORMAL_ARRAY) - + def set_pointer(self, pointer): glNormalPointer(self.gl_type, self.stride, self.offset + pointer) + class SecondaryColorAttribute(AbstractAttribute): - '''Secondary color attribute.''' + """Secondary color attribute.""" plural = 'secondary_colors' _fixed_count = 3 @@ -450,13 +455,14 @@ def enable(self): glEnableClientState(GL_SECONDARY_COLOR_ARRAY) - + def set_pointer(self, pointer): glSecondaryColorPointer(3, self.gl_type, self.stride, self.offset + pointer) + class TexCoordAttribute(AbstractAttribute): - '''Texture coordinate attribute.''' + """Texture coordinate attribute.""" plural = 'tex_coords' @@ -467,19 +473,20 @@ def enable(self): glEnableClientState(GL_TEXTURE_COORD_ARRAY) - + def set_pointer(self, pointer): glTexCoordPointer(self.count, self.gl_type, self.stride, - self.offset + pointer) + self.offset + pointer) def convert_to_multi_tex_coord_attribute(self): - '''Changes the class of the attribute to `MultiTexCoordAttribute`. - ''' + """Changes the class of the attribute to `MultiTexCoordAttribute`. + """ self.__class__ = MultiTexCoordAttribute self.texture = 0 + class MultiTexCoordAttribute(AbstractAttribute): - '''Texture coordinate attribute.''' + """Texture coordinate attribute.""" def __init__(self, texture, count, gl_type): assert gl_type in (GL_SHORT, GL_INT, GL_INT, GL_FLOAT, GL_DOUBLE), \ @@ -490,13 +497,14 @@ def enable(self): glClientActiveTexture(GL_TEXTURE0 + self.texture) glEnableClientState(GL_TEXTURE_COORD_ARRAY) - + def set_pointer(self, pointer): glTexCoordPointer(self.count, self.gl_type, self.stride, - self.offset + pointer) + self.offset + pointer) + class VertexAttribute(AbstractAttribute): - '''Vertex coordinate attribute.''' + """Vertex coordinate attribute.""" plural = 'vertices' @@ -514,8 +522,9 @@ glVertexPointer(self.count, self.gl_type, self.stride, self.offset + pointer) + class GenericAttribute(AbstractAttribute): - '''Generic vertex attribute, used by shader programs.''' + """Generic vertex attribute, used by shader programs.""" def __init__(self, index, normalized, count, gl_type): self.normalized = bool(normalized) @@ -527,8 +536,10 @@ def set_pointer(self, pointer): glVertexAttribPointer(self.index, self.count, self.gl_type, - self.normalized, self.stride, + self.normalized, self.stride, self.offset + pointer) + + _attribute_classes = { 'c': ColorAttribute, 'e': EdgeFlagAttribute, diff -Nru pyglet-1.4.10/pyglet/graphics/vertexbuffer.py pyglet-1.5.14/pyglet/graphics/vertexbuffer.py --- pyglet-1.4.10/pyglet/graphics/vertexbuffer.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/graphics/vertexbuffer.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ """Byte abstractions of Vertex Buffer Objects and vertex arrays. @@ -45,13 +44,9 @@ :py:meth:`~AbstractMappable.get_region` method which provides the most efficient path for updating partial data within the buffer. """ -from builtins import object -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import ctypes import sys +import ctypes import pyglet from pyglet.gl import * @@ -117,7 +112,7 @@ return VertexArray(size) -class AbstractBuffer(object): +class AbstractBuffer: """Abstract buffer of byte data. :Ivariables: @@ -202,7 +197,7 @@ raise NotImplementedError('abstract') -class AbstractMappable(object): +class AbstractMappable: def get_region(self, start, size, ptr_type): """Map a region of the buffer into a ctypes array of the desired type. This region does not need to be unmapped, but will become @@ -444,7 +439,7 @@ self._dirty_max = 0 -class AbstractBufferRegion(object): +class AbstractBufferRegion: """A mapped region of a buffer. Buffer regions are obtained using :py:meth:`~AbstractMappable.get_region`. diff -Nru pyglet-1.4.10/pyglet/graphics/vertexdomain.py pyglet-1.5.14/pyglet/graphics/vertexdomain.py --- pyglet-1.4.10/pyglet/graphics/vertexdomain.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/graphics/vertexdomain.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ """Manage related vertex attributes within a single vertex domain. @@ -56,14 +55,9 @@ :py:meth:`VertexDomain.draw` method, assuming all the vertices comprise primitives of the same OpenGL primitive mode. """ -from builtins import zip -from builtins import object -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -import ctypes import re +import ctypes from pyglet.gl import * from pyglet.graphics import allocation, vertexattribute, vertexbuffer @@ -152,7 +146,7 @@ return IndexedVertexDomain(attribute_usages) -class VertexDomain(object): +class VertexDomain: """Management of a set of vertex lists. Construction of a vertex domain is usually done with the @@ -331,7 +325,7 @@ return '<%s@%x %s>' % (self.__class__.__name__, id(self), self.allocator) -class VertexList(object): +class VertexList: """A list of vertices within a :py:class:`VertexDomain`. Use :py:meth:`VertexDomain.create` to construct this list. """ diff -Nru pyglet-1.4.10/pyglet/gui/frame.py pyglet-1.5.14/pyglet/gui/frame.py --- pyglet-1.4.10/pyglet/gui/frame.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/gui/frame.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,122 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- +import pyglet + +from .ninepatch import NinePatch, NinePatchGroup + + +class Frame: + + def __init__(self, window, cell_size=64, order=0): + window.push_handlers(self) + self._cell_size = cell_size + self._cells = {} + self._active_widgets = set() + self._order = order + self._mouse_pos = 0, 0 + + def _hash(self, x, y): + """Normalize position to cell""" + return int(x / self._cell_size), int(y / self._cell_size) + + def add_widget(self, widget): + """Insert Widget into the appropriate cell""" + min_vec, max_vec = self._hash(*widget.aabb[0:2]), self._hash(*widget.aabb[2:4]) + for i in range(min_vec[0], max_vec[0] + 1): + for j in range(min_vec[1], max_vec[1] + 1): + self._cells.setdefault((i, j), set()).add(widget) + # TODO: return ID and track Widgets for later deletion. + widget.update_groups(self._order) + + def on_mouse_press(self, x, y, buttons, modifiers): + """Pass the event to any widgets within range of the mouse""" + for widget in self._cells.get(self._hash(x, y), set()): + widget.dispatch_event('on_mouse_press', x, y, buttons, modifiers) + self._active_widgets.add(widget) + + def on_mouse_release(self, x, y, buttons, modifiers): + """Pass the event to any widgets that are currently active""" + for widget in self._active_widgets: + widget.dispatch_event('on_mouse_release', x, y, buttons, modifiers) + self._active_widgets.clear() + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + """Pass the event to any widgets that are currently active""" + for widget in self._active_widgets: + widget.dispatch_event('on_mouse_drag', x, y, dx, dy, buttons, modifiers) + self._mouse_pos = x, y + + def on_mouse_scroll(self, x, y, index, direction): + """Pass the event to any widgets within range of the mouse""" + for widget in self._cells.get(self._hash(x, y), set()): + widget.dispatch_event('on_mouse_scroll', x, y, index, direction) + + def on_mouse_motion(self, x, y, dx, dy): + """Pass the event to any widgets within range of the mouse""" + for widget in self._active_widgets: + widget.dispatch_event('on_mouse_motion', x, y, dx, dy) + for widget in self._cells.get(self._hash(x, y), set()): + widget.dispatch_event('on_mouse_motion', x, y, dx, dy) + self._active_widgets.add(widget) + self._mouse_pos = x, y + + def on_text(self, text): + for widget in self._cells.get(self._hash(*self._mouse_pos), set()): + widget.dispatch_event('on_text', text) + + def on_text_motion(self, motion): + for widget in self._cells.get(self._hash(*self._mouse_pos), set()): + widget.dispatch_event('on_text_motion', motion) + + def on_text_motion_select(self, motion): + for widget in self._cells.get(self._hash(*self._mouse_pos), set()): + widget.dispatch_event('on_text_motion_select', motion) + + +class NinePatchFrame(Frame): + + def __init__(self, x, y, width, height, window, image, group=None, batch=None, cell_size=128, order=0): + super().__init__(window, cell_size, order) + self._npatch = NinePatch(image) + self._npatch.get_vertices(x, y, width, height) + self._group = NinePatchGroup(image.get_texture(), order, group) + self._batch = batch or pyglet.graphics.Batch() + + vertices = self._npatch.get_vertices(x, y, width, height) + indices = self._npatch.indices + tex_coords = self._npatch.tex_coords + + self._vlist = self._batch.add_indexed(16, pyglet.gl.GL_QUADS, self._group, indices, + ('v2i', vertices), ('t2f', tex_coords)) diff -Nru pyglet-1.4.10/pyglet/gui/__init__.py pyglet-1.5.14/pyglet/gui/__init__.py --- pyglet-1.4.10/pyglet/gui/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/gui/__init__.py 2020-11-16 14:49:52.000000000 +0000 @@ -0,0 +1,37 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +from .widgets import * +from .frame import * diff -Nru pyglet-1.4.10/pyglet/gui/ninepatch.py pyglet-1.5.14/pyglet/gui/ninepatch.py --- pyglet-1.4.10/pyglet/gui/ninepatch.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/gui/ninepatch.py 2020-12-23 15:47:40.000000000 +0000 @@ -0,0 +1,233 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Draw a NinePatch image. + +NinePatch is a format for storing how to cut up a 9-part resizable +rectangular image within the image pixel data directly. + +For more information on the NinePatch format, see +http://developer.android.com/guide/topics/graphics/2d-graphics.html#nine-patch. + +""" + +from pyglet.graphics import OrderedGroup +from pyglet.gl import GL_BLEND, GL_ENABLE_BIT, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA +from pyglet.gl import glBindTexture, glBlendFunc, glEnable, glPopAttrib, glPushAttrib + + +class NinePatchException(Exception): + pass + + +class NinePatchGroup(OrderedGroup): + def __init__(self, texture, order=0, parent=None): + super().__init__(order, parent) + self.texture = texture + + def set_state(self): + glPushAttrib(GL_ENABLE_BIT) + glEnable(GL_BLEND) + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) + glEnable(self.texture.target) + glBindTexture(self.texture.target, self.texture.id) + + def unset_state(self): + glPopAttrib() + + +class _PixelData: + def __init__(self, image): + image_data = image.get_image_data() + self.has_alpha = 'A' in image_data.format + self.data = image_data.get_data("RGBA", image.width * 4) + self.width = image.width + self.height = image.height + + def is_black(self, x, y): + p = (y * self.width + x) * 4 + if self.has_alpha: + if self.data[p + 3] == 0: + return False # Fully transparent + + return self.data[p:p + 3] == b'\x00\x00\x00' + + +class NinePatch: + """A scalable 9-patch image. + """ + + # Content area of the image, in pixels from the edge. + _padding_top = 0 + _padding_bottom = 0 + _padding_right = 0 + _padding_left = 0 + + # Resizable area of the image, in pixels from the closest edge + _stretch_top = 0 + _stretch_left = 0 + _stretch_right = 0 + _stretch_bottom = 0 + + def __init__(self, image): + """Create NinePatch cuts of an image + + Arguments: + image - an ImageData (Texture, TextureRegion, etc) + texture - force cut ImageDatas to be Textures (or Regions) + """ + + pixel_data = _PixelData(image) + width = image.width + height = pixel_data.height + + # Texture dimensions after removing the 9patch outline. + self.width = width - 2 + self.height = height - 2 + + # Find stretch area markers + for x in range(1, width - 1): + if pixel_data.is_black(x, height - 1): + self._stretch_left = x + break + else: + self._stretch_left = 1 + + for x in range(width - 2, 0, -1): + if pixel_data.is_black(x, height - 1): + self._stretch_right = width - x + break + else: + self._stretch_right = 1 + + for y in range(1, height - 1): + if pixel_data.is_black(0, y): + self._stretch_bottom = y + break + else: + self._stretch_bottom = 1 + + for y in range(height - 2, 0, -1): + if pixel_data.is_black(0, y): + self._stretch_top = height - y + break + else: + self._stretch_top = 1 + + # Find content area markers, if any + for x in range(1, width - 1): + if pixel_data.is_black(x, 0): + self._padding_left = x - 1 + break + + for x in range(width - 2, 0, -1): + if pixel_data.is_black(x, 0): + self._padding_right = self.width - x + break + + for y in range(1, height - 1): + if pixel_data.is_black(width - 1, y): + self._padding_bottom = y - 1 + break + + for y in range(height - 2, 0, -1): + if pixel_data.is_black(width - 1, y): + self._padding_top = self.height - y + break + + # Texture coordinates, in pixels + u1 = 1 + v1 = 1 + u2 = self._stretch_left + 1 + v2 = self._stretch_bottom + 1 + u3 = width - self._stretch_right - 1 + v3 = height - self._stretch_top - 1 + u4 = width - 1 + v4 = height - 1 + + # Texture coordinates as ratio of image size (0 to 1) + u1, u2, u3, u4 = [s / width for s in (u1, u2, u3, u4)] + v1, v2, v3, v4 = [s / height for s in (v1, v2, v3, v4)] + + # Scale texture coordinates to match the tex_coords pyglet gives us + # (these aren't necessarily 0-1 as the texture may have been packed) + (tu1, tv1, ___, + ___, ___, ___, + tu2, tv2, ___, + ___, ___, ___) = image.get_texture().tex_coords + u_scale = tu2 - tu1 + u_bias = tu1 + v_scale = tv2 - tv1 + v_bias = tv1 + u1, u2, u3, u4 = [u_bias + u_scale * s for s in (u1, u2, u3, u4)] + v1, v2, v3, v4 = [v_bias + v_scale * s for s in (v1, v2, v3, v4)] + + # 2D texture coordinates, bottom-left to top-right + self.tex_coords = (u1, v1, u2, v1, u3, v1, u4, v1, + u1, v2, u2, v2, u3, v2, u4, v2, + u1, v3, u2, v3, u3, v3, u4, v3, + u1, v4, u2, v4, u3, v4, u4, v4) + + # Quad indices + self.indices = [] + for y in range(3): + for x in range(3): + self.indices.extend([x + y * 4, (x + 1) + y * 4, (x + 1) + (y + 1) * 4, x + (y + 1) * 4]) + + def get_vertices(self, x, y, width, height): + """Get 16 2D vertices for the given image region""" + x = int(x) + y = int(y) + + if width < self.width + 2 or height < self.height + 2: + raise NinePatchException("Requested size is smaller than the image.") + + width = int(width) + height = int(height) + + x1 = x + y1 = y + x2 = x + self._stretch_left + y2 = y + self._stretch_bottom + x3 = x + width - self._stretch_right + y3 = y + height - self._stretch_top + x4 = x + width + y4 = y + height + + # To match tex coords, vertices are bottom-left to top-right + return (x1, y1, x2, y1, x3, y1, x4, y1, + x1, y2, x2, y2, x3, y2, x4, y2, + x1, y3, x2, y3, x3, y3, x4, y3, + x1, y4, x2, y4, x3, y4, x4, y4) diff -Nru pyglet-1.4.10/pyglet/gui/widgets.py pyglet-1.5.14/pyglet/gui/widgets.py --- pyglet-1.4.10/pyglet/gui/widgets.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/gui/widgets.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,324 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +import pyglet + +from pyglet.event import EventDispatcher +from pyglet.graphics import OrderedGroup +from pyglet.text.caret import Caret +from pyglet.text.layout import IncrementalTextLayout + + +class WidgetBase(EventDispatcher): + + def __init__(self, x, y, width, height): + self._x = x + self._y = y + self._width = width + self._height = height + self._bg_group = None + self._fg_group = None + + def update_groups(self, order): + pass + + @property + def x(self): + return self._x + + @property + def y(self): + return self._y + + @property + def width(self): + return self._width + + @property + def height(self): + return self._height + + @property + def aabb(self): + return self._x, self._y, self._x + self._width, self._y + self._height + + def _check_hit(self, x, y): + return self._x < x < self._x + self._width and self._y < y < self._y + self._height + + def on_mouse_press(self, x, y, buttons, modifiers): + pass + + def on_mouse_release(self, x, y, buttons, modifiers): + pass + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + pass + + def on_mouse_scroll(self, x, y, mouse, direction): + pass + + def on_text(self, text): + pass + + def on_text_motion(self, motion): + pass + + def on_text_motion_select(self, motion): + pass + + +WidgetBase.register_event_type('on_mouse_press') +WidgetBase.register_event_type('on_mouse_release') +WidgetBase.register_event_type('on_mouse_motion') +WidgetBase.register_event_type('on_mouse_scroll') +WidgetBase.register_event_type('on_mouse_drag') +WidgetBase.register_event_type('on_text') +WidgetBase.register_event_type('on_text_motion') +WidgetBase.register_event_type('on_text_motion_select') + + +class PushButton(WidgetBase): + + def __init__(self, x, y, pressed, depressed, hover=None, batch=None, group=None): + super().__init__(x, y, depressed.width, depressed.height) + self._pressed_img = pressed + self._depressed_img = depressed + self._hover_img = hover or depressed + + # TODO: add `draw` method or make Batch required. + self._batch = batch or pyglet.graphics.Batch() + self._user_group = group + bg_group = OrderedGroup(0, parent=group) + self._sprite = pyglet.sprite.Sprite(self._depressed_img, x, y, batch=batch, group=bg_group) + + self._pressed = False + + def update_groups(self, order): + self._sprite.group = OrderedGroup(order + 1, self._user_group) + + def on_mouse_press(self, x, y, buttons, modifiers): + if not self._check_hit(x, y): + return + self._sprite.image = self._pressed_img + self._pressed = True + self.dispatch_event('on_press') + + def on_mouse_release(self, x, y, buttons, modifiers): + if not self._pressed: + return + self._sprite.image = self._hover_img if self._check_hit(x, y) else self._depressed_img + self._pressed = False + self.dispatch_event('on_release') + + def on_mouse_motion(self, x, y, dx, dy): + if self._pressed: + return + self._sprite.image = self._hover_img if self._check_hit(x, y) else self._depressed_img + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + if self._pressed: + return + self._sprite.image = self._hover_img if self._check_hit(x, y) else self._depressed_img + + +PushButton.register_event_type('on_press') +PushButton.register_event_type('on_release') + + +class ToggleButton(PushButton): + + def _get_release_image(self, x, y): + return self._hover_img if self._check_hit(x, y) else self._depressed_img + + def on_mouse_press(self, x, y, buttons, modifiers): + if not self._check_hit(x, y): + return + self._pressed = not self._pressed + self._sprite.image = self._pressed_img if self._pressed else self._get_release_image(x, y) + self.dispatch_event('on_toggle', self._pressed) + + def on_mouse_release(self, x, y, buttons, modifiers): + if self._pressed: + return + self._sprite.image = self._get_release_image(x, y) + + +ToggleButton.register_event_type('on_toggle') + + +class Slider(WidgetBase): + + def __init__(self, x, y, base, knob, edge=0, batch=None, group=None): + super().__init__(x, y, base.width, knob.height) + self._edge = edge + self._base_img = base + self._knob_img = knob + self._half_knob_width = knob.width / 2 + self._half_knob_height = knob.height / 2 + self._knob_img.anchor_y = knob.height / 2 + + self._min_knob_x = x + edge + self._max_knob_x = x + base.width - knob.width - edge + + self._user_group = group + bg_group = OrderedGroup(0, parent=group) + fg_group = OrderedGroup(1, parent=group) + self._base_spr = pyglet.sprite.Sprite(self._base_img, x, y, batch=batch, group=bg_group) + self._knob_spr = pyglet.sprite.Sprite(self._knob_img, x+edge, y+base.height/2, batch=batch, group=fg_group) + + self._value = 0 + self._in_update = False + + def update_groups(self, order): + self._base_spr.group = OrderedGroup(order + 1, self._user_group) + self._knob_spr.group = OrderedGroup(order + 2, self._user_group) + + @property + def _min_x(self): + return self._x + self._edge + + @property + def _max_x(self): + return self._x + self._width - self._edge + + @property + def _min_y(self): + return self._y - self._half_knob_height + + @property + def _max_y(self): + return self._y + self._half_knob_height + self._base_img.height / 2 + + def _check_hit(self, x, y): + return self._min_x < x < self._max_x and self._min_y < y < self._max_y + + def _update_knob(self, x): + self._knob_spr.x = max(self._min_knob_x, min(x - self._half_knob_width, self._max_knob_x)) + self._value = abs(((self._knob_spr.x - self._min_knob_x) * 100) / (self._min_knob_x - self._max_knob_x)) + self.dispatch_event('on_change', self._value) + + def on_mouse_press(self, x, y, buttons, modifiers): + if self._check_hit(x, y): + self._in_update = True + self._update_knob(x) + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + if self._in_update: + self._update_knob(x) + + def on_mouse_scroll(self, x, y, mouse, direction): + if self._check_hit(x, y): + self._update_knob(self._knob_spr.x + self._half_knob_width + direction) + + def on_mouse_release(self, x, y, buttons, modifiers): + self._in_update = False + + +Slider.register_event_type('on_change') + + +class TextEntry(WidgetBase): + + def __init__(self, text, x, y, width, color=(255, 255, 255, 255), batch=None, group=None): + self._doc = pyglet.text.document.UnformattedDocument(text) + self._doc.set_style(0, len(self._doc.text), dict(color=(0, 0, 0, 255))) + font = self._doc.get_font() + height = font.ascent - font.descent + + self._user_group = group + bg_group = OrderedGroup(0, parent=group) + fg_group = OrderedGroup(1, parent=group) + + # Rectangular outline with 2-pixel pad: + p = 2 + self._outline = pyglet.shapes.Rectangle(x-p, y-p, width+p+p, height+p+p, color[:3], batch, bg_group) + self._outline.opacity = color[3] + + # Text and Caret: + self._layout = IncrementalTextLayout(self._doc, width, height, multiline=False, batch=batch, group=fg_group) + self._layout.x = x + self._layout.y = y + self._caret = Caret(self._layout) + self._caret.visible = False + + self._focus = False + + super().__init__(x, y, width, height) + + def _check_hit(self, x, y): + return self._x < x < self._x + self._width and self._y < y < self._y + self._height + + def _set_focus(self, value): + self._focus = value + self._caret.visible = value + + def update_groups(self, order): + self._outline.group = OrderedGroup(order + 1, self._user_group) + self._layout.group = OrderedGroup(order + 2, self._user_group) + + def on_mouse_motion(self, x, y, dx, dy): + if not self._check_hit(x, y): + self._set_focus(False) + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + if self._focus: + self._caret.on_mouse_drag(x, y, dx, dy, buttons, modifiers) + + def on_mouse_press(self, x, y, buttons, modifiers): + if self._check_hit(x, y): + self._set_focus(True) + self._caret.on_mouse_press(x, y, buttons, modifiers) + + def on_text(self, text): + if self._focus: + if text in ('\r', '\n'): + self.dispatch_event('on_commit', self._layout.document.text) + self._set_focus(False) + return + self._caret.on_text(text) + + def on_text_motion(self, motion): + if self._focus: + self._caret.on_text_motion(motion) + + def on_text_motion_select(self, motion): + if self._focus: + self._caret.on_text_motion_select(motion) + + def on_commit(self, text): + """Text has been commited via Enter/Return key.""" + + +TextEntry.register_event_type('on_commit') diff -Nru pyglet-1.4.10/pyglet/image/animation.py pyglet-1.5.14/pyglet/image/animation.py --- pyglet-1.4.10/pyglet/image/animation.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/animation.py 2020-12-23 15:47:40.000000000 +0000 @@ -76,7 +76,7 @@ """ -class Animation(object): +class Animation: """Sequence of images with timing information. If no frames of the animation have a duration of ``None``, the animation @@ -100,7 +100,7 @@ assert len(frames) self.frames = frames - def add_to_texture_bin(self, texture_bin): + def add_to_texture_bin(self, texture_bin, border=0): """Add the images of the animation to a :py:class:`~pyglet.image.atlas.TextureBin`. The animation frames are modified in-place to refer to the texture bin @@ -109,10 +109,13 @@ :Parameters: `texture_bin` : `~pyglet.image.atlas.TextureBin` Texture bin to upload animation frames into. + `border` : int + Leaves specified pixels of blank space around + each image frame when adding to the TextureBin. """ for frame in self.frames: - frame.image = texture_bin.add(frame.image) + frame.image = texture_bin.add(frame.image, border) def get_transform(self, flip_x=False, flip_y=False, rotate=0): """Create a copy of this animation applying a simple transformation. @@ -166,20 +169,20 @@ return max([frame.image.height for frame in self.frames]) @classmethod - def from_image_sequence(cls, sequence, period, loop=True): + def from_image_sequence(cls, sequence, duration, loop=True): """Create an animation from a list of images and a constant framerate. :Parameters: `sequence` : list of `~pyglet.image.AbstractImage` Images that make up the animation, in sequence. - `period` : float + `duration` : float Number of seconds to display each image. `loop` : bool If True, the animation will loop continuously. :rtype: :py:class:`~pyglet.image.Animation` """ - frames = [AnimationFrame(image, period) for image in sequence] + frames = [AnimationFrame(image, duration) for image in sequence] if not loop: frames[-1].duration = None return cls(frames) @@ -188,7 +191,7 @@ return "Animation(frames={0})".format(len(self.frames)) -class AnimationFrame(object): +class AnimationFrame: """A single frame of an animation.""" __slots__ = 'image', 'duration' diff -Nru pyglet-1.4.10/pyglet/image/atlas.py pyglet-1.5.14/pyglet/image/atlas.py --- pyglet-1.4.10/pyglet/image/atlas.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/atlas.py 2020-12-23 15:47:40.000000000 +0000 @@ -58,11 +58,6 @@ .. versionadded:: 1.1 """ -from __future__ import division -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import pyglet @@ -75,7 +70,7 @@ pass -class _Strip(object): +class _Strip: __slots__ = 'x', 'y', 'max_height', 'y2' def __init__(self, y, max_height): @@ -97,7 +92,7 @@ self.max_height = self.y2 - self.y -class Allocator(object): +class Allocator: """Rectangular area allocation algorithm. Initialise with a given ``width`` and ``height``, then repeatedly @@ -180,10 +175,10 @@ return 1.0 - self.used_area / float(possible_area) -class TextureAtlas(object): +class TextureAtlas: """Collection of images within a texture.""" - def __init__(self, width=2048, height=2048, border=False): + def __init__(self, width=2048, height=2048): """Create a texture atlas of the given size. :Parameters: @@ -191,9 +186,6 @@ Width of the underlying texture. `height` : int Height of the underlying texture. - `border` : bool - If True, one pixel of blank space is left - around each image added to the Atlas. """ max_texture_size = pyglet.image.get_max_texture_size() @@ -202,9 +194,8 @@ self.texture = pyglet.image.Texture.create(width, height, GL_RGBA, rectangle=True) self.allocator = Allocator(width, height) - self._border = 1 if border else 0 - def add(self, img): + def add(self, img, border=0): """Add an image to the atlas. This method will fail if the given image cannot be transferred @@ -217,24 +208,26 @@ :Parameters: `img` : `~pyglet.image.AbstractImage` The image to add. + `border` : int + Leaves specified pixels of blank space around + each image added to the Atlas. :rtype: :py:class:`~pyglet.image.TextureRegion` :return: The region of the atlas containing the newly added image. """ - b = self._border - x, y = self.allocator.alloc(img.width + b*2, img.height + b*2) - self.texture.blit_into(img, x+b, y+b, 0) - return self.texture.get_region(x+b, y+b, img.width, img.height) + x, y = self.allocator.alloc(img.width + border*2, img.height + border*2) + self.texture.blit_into(img, x+border, y+border, 0) + return self.texture.get_region(x+border, y+border, img.width, img.height) -class TextureBin(object): +class TextureBin: """Collection of texture atlases. :py:class:`~pyglet.image.atlas.TextureBin` maintains a collection of texture atlases, and creates new ones as necessary to accommodate images added to the bin. """ - def __init__(self, texture_width=2048, texture_height=2048, border=False): + def __init__(self, texture_width=2048, texture_height=2048): """Create a texture bin for holding atlases of the given size. :Parameters: @@ -242,18 +235,17 @@ Width of texture atlases to create. `texture_height` : int Height of texture atlases to create. - `border` : bool - If True, one pixel of blank space is left - around each image added to the Atlases. + `border` : int + Leaves specified pixels of blank space around + each image added to the Atlases. """ max_texture_size = pyglet.image.get_max_texture_size() self.texture_width = min(texture_width, max_texture_size) self.texture_height = min(texture_height, max_texture_size) self.atlases = [] - self._border = border - def add(self, img): + def add(self, img, border=0): """Add an image into this texture bin. This method calls `TextureAtlas.add` for the first atlas that has room @@ -265,13 +257,16 @@ :Parameters: `img` : `~pyglet.image.AbstractImage` The image to add. + `border` : int + Leaves specified pixels of blank space around + each image added to the Atlas. :rtype: :py:class:`~pyglet.image.TextureRegion` :return: The region of an atlas containing the newly added image. """ for atlas in list(self.atlases): try: - return atlas.add(img) + return atlas.add(img, border) except AllocatorException: # Remove atlases that are no longer useful (this is so their # textures can later be freed if the images inside them get @@ -281,4 +276,4 @@ atlas = TextureAtlas(self.texture_width, self.texture_height) self.atlases.append(atlas) - return atlas.add(img) + return atlas.add(img, border) diff -Nru pyglet-1.4.10/pyglet/image/codecs/bmp.py pyglet-1.5.14/pyglet/image/codecs/bmp.py --- pyglet-1.4.10/pyglet/image/codecs/bmp.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/bmp.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,12 +33,11 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Decoder for BMP files. +"""Decoder for BMP files. Currently supports version 3 and 4 bitmaps with BI_RGB and BI_BITFIELDS encoding. Alpha channel is supported for 32-bit BI_RGB only. -''' -from builtins import range +""" # Official docs are at # http://msdn2.microsoft.com/en-us/library/ms532311.aspx @@ -46,9 +45,6 @@ # But some details including alignment and bit/byte order are omitted; see # http://www.fileformat.info/format/bmp/egff.htm -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - import ctypes from pyglet.image import ImageData diff -Nru pyglet-1.4.10/pyglet/image/codecs/dds.py pyglet-1.5.14/pyglet/image/codecs/dds.py --- pyglet-1.4.10/pyglet/image/codecs/dds.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/dds.py 2020-11-10 10:36:26.000000000 +0000 @@ -38,20 +38,13 @@ Reference: http://msdn2.microsoft.com/en-us/library/bb172993.aspx """ -from __future__ import division -from __future__ import print_function -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - import struct +import itertools from pyglet.gl import * from pyglet.image import CompressedImageData from pyglet.image import codecs from pyglet.image.codecs import s3tc, ImageDecodeException -from pyglet.compat import izip_longest as compat_izip_longest # dwFlags of DDSURFACEDESC2 @@ -85,14 +78,14 @@ DDSCAPS2_VOLUME = 0x00200000 -class _FileStruct(object): +class _FileStruct: _fields = [] def __init__(self, data): if len(data) < self.get_size(): raise ImageDecodeException('Not a DDS file') items = struct.unpack(self.get_format(), data) - for field, value in compat_izip_longest(self._fields, items, fillvalue=None): + for field, value in itertools.zip_longest(self._fields, items, fillvalue=None): setattr(self, field[0], value) def __repr__(self): diff -Nru pyglet-1.4.10/pyglet/image/codecs/gdiplus.py pyglet-1.5.14/pyglet/image/codecs/gdiplus.py --- pyglet-1.4.10/pyglet/image/codecs/gdiplus.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/gdiplus.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,25 +32,18 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import division -from builtins import range - -from ctypes import * - -from pyglet.com import IUnknown -from pyglet.gl import * +from pyglet.com import pIUnknown from pyglet.image import * from pyglet.image.codecs import * from pyglet.libs.win32.constants import * from pyglet.libs.win32.types import * from pyglet.libs.win32 import _kernel32 as kernel32 +from pyglet.libs.win32 import _ole32 as ole32 -ole32 = windll.ole32 gdiplus = windll.gdiplus -LPSTREAM = c_void_p REAL = c_float PixelFormat1bppIndexed = 196865 @@ -124,7 +117,6 @@ INT_PTR = POINTER(INT) UINT_PTR = POINTER(UINT) -ole32.CreateStreamOnHGlobal.argtypes = [HGLOBAL, BOOL, LPSTREAM] gdiplus.GdipBitmapLockBits.restype = c_int gdiplus.GdipBitmapLockBits.argtypes = [c_void_p, c_void_p, UINT, c_int, c_void_p] @@ -216,7 +208,7 @@ kernel32.GlobalUnlock(hglob) # Create IStream for the HGLOBAL - self.stream = IUnknown() + self.stream = pIUnknown() ole32.CreateStreamOnHGlobal(hglob, True, byref(self.stream)) # Load image from stream diff -Nru pyglet-1.4.10/pyglet/image/codecs/gdkpixbuf2.py pyglet-1.5.14/pyglet/image/codecs/gdkpixbuf2.py --- pyglet-1.4.10/pyglet/image/codecs/gdkpixbuf2.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/gdkpixbuf2.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,7 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from builtins import object from ctypes import * from pyglet.gl import * @@ -89,7 +88,7 @@ error.contents.message) -class GdkPixBufLoader(object): +class GdkPixBufLoader: """ Wrapper around GdkPixBufLoader object. """ @@ -157,7 +156,7 @@ return [image.delay for image in gif_stream.images] -class GdkPixBuf(object): +class GdkPixBuf: """ Wrapper around GdkPixBuf object. """ @@ -221,7 +220,7 @@ return ImageData(self.width, self.height, format, pixels, -self.rowstride) -class GdkPixBufAnimation(object): +class GdkPixBufAnimation: """ Wrapper for a GdkPixBufIter for an animation. """ @@ -244,7 +243,7 @@ return Animation(list(self)) -class GdkPixBufAnimationIterator(object): +class GdkPixBufAnimationIterator: def __init__(self, loader, anim_iter, start_time, gif_delays): self._iter = anim_iter self._first = True diff -Nru pyglet-1.4.10/pyglet/image/codecs/gif.py pyglet-1.5.14/pyglet/image/codecs/gif.py --- pyglet-1.4.10/pyglet/image/codecs/gif.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/gif.py 2020-11-10 10:36:26.000000000 +0000 @@ -37,28 +37,22 @@ http://www.w3.org/Graphics/GIF/spec-gif89a.txt """ -from __future__ import print_function -from __future__ import division -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' import struct from pyglet.image.codecs import ImageDecodeException -class GIFStream(object): +class GIFStream: def __init__(self): self.images = [] -class GIFImage(object): +class GIFImage: delay = None -class GraphicsScope(object): +class GraphicsScope: delay = None diff -Nru pyglet-1.4.10/pyglet/image/codecs/__init__.py pyglet-1.5.14/pyglet/image/codecs/__init__.py --- pyglet-1.4.10/pyglet/image/codecs/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/__init__.py 2020-11-16 14:49:52.000000000 +0000 @@ -49,35 +49,61 @@ return [] """ -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import os.path + +from pyglet.util import Codecs, Decoder, Encoder, DecodeException, EncodeException from pyglet import compat_platform -_decoders = [] # List of registered ImageDecoders -_decoder_extensions = {} # Map str -> list of matching ImageDecoders -_decoder_animation_extensions = {} # Map str -> list of matching ImageDecoders -_encoders = [] # List of registered ImageEncoders -_encoder_extensions = {} # Map str -> list of matching ImageEncoders +class _ImageCodecs(Codecs): + """Subclass of Codecs that adds support for animation methods.""" -class ImageDecodeException(Exception): - exception_priority = 10 + def __init__(self): + self._decoder_animation_extensions = {} + super().__init__() + + def add_decoders(self, module): + """Override the default method to also add animation decoders. + """ + super().add_decoders(module) + for decoder in module.get_decoders(): + for extension in decoder.get_animation_file_extensions(): + if extension not in self._decoder_animation_extensions: + self._decoder_animation_extensions[extension] = [] + self._decoder_animation_extensions[extension].append(decoder) + + def get_animation_decoders(self, filename=None): + """Get an ordered list of all animation decoders. If a `filename` is + provided, decoders supporting that extension will be ordered first + in the list. + """ + decoders = [] + if filename: + extension = os.path.splitext(filename)[1].lower() + decoders += self._decoder_animation_extensions.get(extension, []) + decoders += [e for e in self._decoders if e not in decoders] + return decoders + + +_codecs = _ImageCodecs() + +add_decoders = _codecs.add_decoders +get_decoders = _codecs.get_decoders +add_encoders = _codecs.add_encoders +get_encoders = _codecs.get_encoders +get_animation_decoders = _codecs.get_animation_decoders -class ImageEncodeException(Exception): +class ImageDecodeException(DecodeException): pass -class ImageDecoder(object): - def get_file_extensions(self): - """Return a list of accepted file extensions, e.g. ['.png', '.bmp'] - Lower-case only. - """ - return [] +class ImageEncodeException(EncodeException): + pass + + +class ImageDecoder(Decoder): def get_animation_file_extensions(self): """Return a list of accepted file extensions, e.g. ['.gif', '.flc'] @@ -105,12 +131,7 @@ self.get_file_extensions()) -class ImageEncoder(object): - def get_file_extensions(self): - """Return a list of accepted file extensions, e.g. ['.png', '.bmp'] - Lower-case only. - """ - return [] +class ImageEncoder(Encoder): def encode(self, image, file, filename): """Encode the given image to the given file. filename @@ -122,72 +143,6 @@ return "{0}{1}".format(self.__class__.__name__, self.get_file_extensions()) -def get_encoders(filename=None): - """Get an ordered list of all encoders. If a `filename` is provided, - encoders supporting that extension will be ordered first in the list. - """ - encoders = [] - if filename: - extension = os.path.splitext(filename)[1].lower() - encoders += _encoder_extensions.get(extension, []) - encoders += [e for e in _encoders if e not in encoders] - return encoders - - -def get_decoders(filename=None): - """Get an ordered list of all decoders. If a `filename` is provided, - decoders supporting that extension will be ordered first in the list. - """ - decoders = [] - if filename: - extension = os.path.splitext(filename)[1].lower() - decoders += _decoder_extensions.get(extension, []) - decoders += [e for e in _decoders if e not in decoders] - return decoders - - -def get_animation_decoders(filename=None): - """Get an ordered list of all decoders. If a `filename` is provided, - decoders supporting that extension will be ordered first in the list. - """ - decoders = [] - if filename: - extension = os.path.splitext(filename)[1].lower() - decoders += _decoder_animation_extensions.get(extension, []) - decoders += [e for e in _decoders if e not in decoders] - return decoders - - -def add_decoders(module): - """Add a decoder module. The module must define `get_decoders`. Once - added, the appropriate decoders defined in the codec will be returned by - pyglet.image.codecs.get_decoders. - """ - for decoder in module.get_decoders(): - _decoders.append(decoder) - for extension in decoder.get_file_extensions(): - if extension not in _decoder_extensions: - _decoder_extensions[extension] = [] - _decoder_extensions[extension].append(decoder) - for extension in decoder.get_animation_file_extensions(): - if extension not in _decoder_animation_extensions: - _decoder_animation_extensions[extension] = [] - _decoder_animation_extensions[extension].append(decoder) - - -def add_encoders(module): - """Add an encoder module. The module must define `get_encoders`. Once - added, the appropriate encoders defined in the codec will be returned by - pyglet.image.codecs.get_encoders. - """ - for encoder in module.get_encoders(): - _encoders.append(encoder) - for extension in encoder.get_file_extensions(): - if extension not in _encoder_extensions: - _encoder_extensions[extension] = [] - _encoder_extensions[extension].append(encoder) - - def add_default_image_codecs(): # Add the codecs we know about. These should be listed in order of # preference. This is called automatically by pyglet.image. @@ -209,6 +164,17 @@ except ImportError: pass + # Windows 7 default: Windows Imaging Component + if compat_platform in ('win32', 'cygwin'): + from pyglet.libs.win32.constants import WINDOWS_7_OR_GREATER + if WINDOWS_7_OR_GREATER: # Supports Vista and above. + try: + from pyglet.image.codecs import wic + add_encoders(wic) + add_decoders(wic) + except ImportError: + pass + # Windows XP default: GDI+ if compat_platform in ('win32', 'cygwin'): try: diff -Nru pyglet-1.4.10/pyglet/image/codecs/pil.py pyglet-1.5.14/pyglet/image/codecs/pil.py --- pyglet-1.4.10/pyglet/image/codecs/pil.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/pil.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,10 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' import os.path @@ -79,7 +75,10 @@ # tostring is deprecated, replaced by tobytes in Pillow (PIL fork) # (1.1.7) PIL still uses it - image_data_fn = getattr(image, "tobytes", getattr(image, "tostring")) + try: + image_data_fn = getattr(image, "tobytes") + except AttributeError: + image_data_fn = getattr(image, "tostring") return ImageData(width, height, image.mode, image_data_fn()) # def decode_animation(self, file, filename): @@ -136,7 +135,10 @@ # fromstring is deprecated, replaced by frombytes in Pillow (PIL fork) # (1.1.7) PIL still uses it - image_from_fn = getattr(Image, "frombytes", getattr(Image, "fromstring")) + try: + image_from_fn = getattr(Image, "frombytes") + except AttributeError: + image_from_fn = getattr(Image, "fromstring") pil_image = image_from_fn(fmt, (image.width, image.height), image.get_data(fmt, pitch)) try: diff -Nru pyglet-1.4.10/pyglet/image/codecs/png.py pyglet-1.5.14/pyglet/image/codecs/png.py --- pyglet-1.4.10/pyglet/image/codecs/png.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/png.py 2020-12-31 21:03:32.000000000 +0000 @@ -70,7 +70,7 @@ pitch = len(fmt) * width pixels = array.array('BH'[metadata['bitdepth'] > 8], itertools.chain(*pixels)) - return ImageData(width, height, fmt, pixels.tostring(), -pitch) + return ImageData(width, height, fmt, pixels.tobytes(), -pitch) class PNGImageEncoder(ImageEncoder): diff -Nru pyglet-1.4.10/pyglet/image/codecs/quartz.py pyglet-1.5.14/pyglet/image/codecs/quartz.py --- pyglet-1.4.10/pyglet/image/codecs/quartz.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/quartz.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,13 +33,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' from ctypes import c_void_p, c_ubyte -from builtins import range - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' from pyglet.image import ImageData, Animation, AnimationFrame from pyglet.image.codecs import * diff -Nru pyglet-1.4.10/pyglet/image/codecs/s3tc.py pyglet-1.5.14/pyglet/image/codecs/s3tc.py --- pyglet-1.4.10/pyglet/image/codecs/s3tc.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/s3tc.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,14 +32,11 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ -'''Software decoder for S3TC compressed texture (i.e., DDS). +"""Software decoder for S3TC compressed texture (i.e., DDS). http://oss.sgi.com/projects/ogl-sample/registry/EXT/texture_compression_s3tc.txt -''' -from __future__ import division -from builtins import range +""" import ctypes import re @@ -51,6 +48,7 @@ split_8byte = re.compile('.' * 8, flags=re.DOTALL) split_16byte = re.compile('.' * 16, flags=re.DOTALL) + class PackedImageData(AbstractImage): _current_texture = None @@ -95,11 +93,12 @@ texture = property(_get_texture) def get_texture(self, rectangle=False, force_rectangle=False): - '''The parameters 'rectangle' and 'force_rectangle' are ignored. + """The parameters 'rectangle' and 'force_rectangle' are ignored. See the documentation of the method 'AbstractImage.get_texture' for - a more detailed documentation of the method. ''' + a more detailed documentation of the method. """ return self._get_texture() + def decode_dxt1_rgb(data, width, height): # Decode to 16-bit RGB UNSIGNED_SHORT_5_6_5 out = (ctypes.c_uint16 * (width * height))() @@ -154,8 +153,8 @@ advance_row = (image_offset + 4) % width == 0 image_offset += width * 3 * advance_row + 4 - return PackedImageData(width, height, - GL_RGB, GL_UNSIGNED_SHORT_5_6_5, out) + return PackedImageData(width, height, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, out) + def decode_dxt1_rgba(data, width, height): # Decode to GL_RGBA @@ -287,6 +286,7 @@ return PackedImageData(width, height, GL_RGBA, GL_UNSIGNED_BYTE, out) + def decode_dxt5(data, width, height): # Decode to GL_RGBA out = (ctypes.c_ubyte * (width * height * 4))() diff -Nru pyglet-1.4.10/pyglet/image/codecs/wic.py pyglet-1.5.14/pyglet/image/codecs/wic.py --- pyglet-1.4.10/pyglet/image/codecs/wic.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/codecs/wic.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,415 @@ +import warnings +from pyglet.image import * +from pyglet.image.codecs import * +from pyglet import com +from pyglet.libs.win32.constants import * +from pyglet.libs.win32.types import * +from pyglet.libs.win32 import _kernel32 as kernel32 +from pyglet.libs.win32 import _ole32 as ole32 + +CLSID_WICImagingFactory1 = com.GUID(0xcacaf262, 0x9370, 0x4615, 0xa1, 0x3b, 0x9f, 0x55, 0x39, 0xda, 0x4c, 0xa) +CLSID_WICImagingFactory2 = com.GUID(0x317d06e8, 0x5f24, 0x433d, 0xbd, 0xf7, 0x79, 0xce, 0x68, 0xd8, 0xab, 0xc2) + +# This is available with Windows 7 with a Platform Update, but unable to detect as it wasn't a version change to the OS, +# but a KB update. Available in atleast 8+. +if WINDOWS_8_OR_GREATER: + CLSID_WICImagingFactory = CLSID_WICImagingFactory2 +else: + CLSID_WICImagingFactory = CLSID_WICImagingFactory1 + +WICBitmapCreateCacheOption = UINT +WICBitmapNoCache = 0 +WICBitmapCacheOnDemand = 0x1 +WICBitmapCacheOnLoad = 0x2 +WICBITMAPCREATECACHEOPTION_FORCE_DWORD = 0x7fffffff + +WICBitmapPaletteType = UINT +WICBitmapPaletteTypeCustom = 0 + +WICBitmapTransformOptions = UINT +WICBitmapTransformRotate0 = 0 +WICBitmapTransformRotate90 = 0x1 +WICBitmapTransformRotate180 = 0x2 +WICBitmapTransformRotate270 = 0x3 +WICBitmapTransformFlipHorizontal = 0x8 +WICBitmapTransformFlipVertical = 0x10 + +WICBitmapDitherType = UINT +WICBitmapDitherTypeNone = 0 +WICBitmapDitherTypeSolid = 0 +WICBitmapDitherTypeOrdered4x4 = 0x1 +WICBitmapDitherTypeOrdered8x8 = 0x2 +WICBitmapDitherTypeOrdered16x16 = 0x3 +WICBitmapDitherTypeSpiral4x4 = 0x4 +WICBitmapDitherTypeSpiral8x8 = 0x5 +WICBitmapDitherTypeDualSpiral4x4 = 0x6 +WICBitmapDitherTypeDualSpiral8x8 = 0x7 +WICBitmapDitherTypeErrorDiffusion = 0x8 +WICBITMAPDITHERTYPE_FORCE_DWORD = 0x7fffffff +WICBITMAPTRANSFORMOPTIONS_FORCE_DWORD = 0x7fffffff + + +WICDecodeOptions = UINT +WICDecodeMetadataCacheOnDemand = 0 +WICDecodeMetadataCacheOnLoad = 0x1 +WICMETADATACACHEOPTION_FORCE_DWORD = 0x7fffffff + +# Different pixel formats. +REFWICPixelFormatGUID = com.GUID +GUID_WICPixelFormat1bppIndexed = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x01) +GUID_WICPixelFormat2bppIndexed = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x02) +GUID_WICPixelFormat4bppIndexed = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x03) +GUID_WICPixelFormat8bppIndexed = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x04) +GUID_WICPixelFormatBlackWhite = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x05) +GUID_WICPixelFormat2bppGray = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x06) +GUID_WICPixelFormat4bppGray = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x07) +GUID_WICPixelFormat8bppGray = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x08) +GUID_WICPixelFormat8bppAlpha = com.GUID(0xe6cd0116, 0xeeba, 0x4161, 0xaa, 0x85, 0x27, 0xdd, 0x9f, 0xb3, 0xa8, 0x95) +GUID_WICPixelFormat16bppBGR555 = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x09) +GUID_WICPixelFormat16bppBGR565 = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0a) +GUID_WICPixelFormat16bppBGRA5551 = com.GUID(0x05ec7c2b, 0xf1e6, 0x4961, 0xad, 0x46, 0xe1, 0xcc, 0x81, 0x0a, 0x87, 0xd2) +GUID_WICPixelFormat16bppGray = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0b) +GUID_WICPixelFormat24bppBGR = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0c) +GUID_WICPixelFormat24bppRGB = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0d) +GUID_WICPixelFormat32bppBGR = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0e) +GUID_WICPixelFormat32bppBGRA = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x0f) +GUID_WICPixelFormat32bppPBGRA = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x10) +GUID_WICPixelFormat32bppRGB = com.GUID(0xd98c6b95, 0x3efe, 0x47d6, 0xbb, 0x25, 0xeb, 0x17, 0x48, 0xab, 0x0c, 0xf1) # 7 platform update? +GUID_WICPixelFormat32bppRGBA = com.GUID(0xf5c7ad2d, 0x6a8d, 0x43dd, 0xa7, 0xa8, 0xa2, 0x99, 0x35, 0x26, 0x1a, 0xe9) +GUID_WICPixelFormat32bppPRGBA = com.GUID(0x3cc4a650, 0xa527, 0x4d37, 0xa9, 0x16, 0x31, 0x42, 0xc7, 0xeb, 0xed, 0xba) +GUID_WICPixelFormat48bppRGB = com.GUID(0x6fddc324, 0x4e03, 0x4bfe, 0xb1, 0x85, 0x3d, 0x77, 0x76, 0x8d, 0xc9, 0x15) +GUID_WICPixelFormat48bppBGR = com.GUID(0xe605a384, 0xb468, 0x46ce, 0xbb, 0x2e, 0x36, 0xf1, 0x80, 0xe6, 0x43, 0x13) + + +class IWICComponentInfo(com.pIUnknown): + _methods_ = [ + ('GetComponentType', + com.STDMETHOD()), + ('GetCLSID', + com.STDMETHOD()), + ('GetSigningStatus', + com.STDMETHOD()), + ('GetAuthor', + com.STDMETHOD()), + ('GetVendorGUID', + com.STDMETHOD()), + ('GetVersion', + com.STDMETHOD()), + ('GetSpecVersion', + com.STDMETHOD()), + ('GetFriendlyName', + com.STDMETHOD()) + ] + + +class IWICPixelFormatInfo(IWICComponentInfo, com.pIUnknown): + _methods_ = [ + ('GetFormatGUID', + com.STDMETHOD(POINTER(com.GUID))), + ('GetColorContext', + com.STDMETHOD()), + ('GetBitsPerPixel', + com.STDMETHOD(POINTER(UINT))), + ('GetChannelCount', + com.STDMETHOD(POINTER(UINT))), + ('GetChannelMask', + com.STDMETHOD()) + ] + + +class IWICBitmapSource(com.pIUnknown): + _methods_ = [ + ('GetSize', + com.STDMETHOD(POINTER(UINT), POINTER(UINT))), + ('GetPixelFormat', + com.STDMETHOD(POINTER(REFWICPixelFormatGUID))), + ('GetResolution', + com.STDMETHOD(POINTER(DOUBLE), POINTER(DOUBLE))), + ('CopyPalette', + com.STDMETHOD()), + ('CopyPixels', + com.STDMETHOD(c_void_p, UINT, UINT, c_void_p)), + ] + + +class IWICFormatConverter(IWICBitmapSource, com.pIUnknown): + _methods_ = [ + ('Initialize', + com.STDMETHOD(IWICBitmapSource, POINTER(REFWICPixelFormatGUID), WICBitmapDitherType, c_void_p, DOUBLE, WICBitmapPaletteType)), + ('CanConvert', + com.STDMETHOD(POINTER(REFWICPixelFormatGUID), POINTER(REFWICPixelFormatGUID), POINTER(BOOL))), + ] + + +class IWICMetadataQueryReader(com.pIUnknown): + _methods_ = [ + ('GetContainerFormat', + com.STDMETHOD()), + ('GetLocation', + com.STDMETHOD()), + ('GetMetadataByName', + com.STDMETHOD(LPCWSTR, c_void_p)), + ('GetEnumerator', + com.STDMETHOD()), + ] + + +class IWICBitmapFrameDecode(IWICBitmapSource, com.pIUnknown): + _methods_ = [ + ('GetMetadataQueryReader', + com.STDMETHOD(POINTER(IWICMetadataQueryReader))), + ('GetColorContexts', + com.STDMETHOD()), + ('GetThumbnail', + com.STDMETHOD(POINTER(IWICBitmapSource))), + ] + + +class IWICBitmapFlipRotator(IWICBitmapSource, com.pIUnknown): + _methods_ = [ + ('Initialize', + com.STDMETHOD(IWICBitmapSource, WICBitmapTransformOptions)), + ] + + +class IWICBitmap(IWICBitmapSource, com.pIUnknown): + _methods_ = [ + ('Lock', + com.STDMETHOD()), + ('SetPalette', + com.STDMETHOD()), + ('SetResolution', + com.STDMETHOD()) + ] + + +class IWICBitmapDecoder(com.pIUnknown): + _methods_ = [ + ('QueryCapability', + com.STDMETHOD()), + ('Initialize', + com.STDMETHOD()), + ('GetContainerFormat', + com.STDMETHOD()), + ('GetDecoderInfo', + com.STDMETHOD()), + ('CopyPalette', + com.STDMETHOD()), + ('GetMetadataQueryReader', + com.STDMETHOD(POINTER(IWICMetadataQueryReader))), + ('GetPreview', + com.STDMETHOD()), + ('GetColorContexts', + com.STDMETHOD()), + ('GetThumbnail', + com.STDMETHOD()), + ('GetFrameCount', + com.STDMETHOD(POINTER(UINT))), + ('GetFrame', + com.STDMETHOD(UINT, POINTER(IWICBitmapFrameDecode))), + ] + + +IID_IWICImagingFactory1 = com.GUID(0xec5ec8a9, 0xc395, 0x4314, 0x9c, 0x77, 0x54, 0xd7, 0xa9, 0x35, 0xff, 0x70) +IID_IWICImagingFactory2 = com.GUID(0x7B816B45, 0x1996, 0x4476, 0xB1, 0x32, 0xDE, 0x9E, 0x24, 0x7C, 0x8A, 0xF0) + +if WINDOWS_8_OR_GREATER: + IID_IWICImagingFactory = IID_IWICImagingFactory2 +else: + IID_IWICImagingFactory = IID_IWICImagingFactory1 + +IID_IWICPixelFormatInfo = com.GUID(0xE8EDA601, 0x3D48, 0x431a, 0xAB, 0x44, 0x69, 0x05, 0x9B, 0xE8, 0x8B, 0xBE) + + +class IWICImagingFactory(com.pIUnknown): + _methods_ = [ + ('CreateDecoderFromFilename', + com.STDMETHOD(LPCWSTR, com.GUID, DWORD, WICDecodeOptions, POINTER(IWICBitmapDecoder))), + ('CreateDecoderFromStream', + com.STDMETHOD(com.pIUnknown, c_void_p, WICDecodeOptions, POINTER(IWICBitmapDecoder))), + ('CreateDecoderFromFileHandle', + com.STDMETHOD()), + ('CreateComponentInfo', + com.STDMETHOD(com.GUID, POINTER(IWICComponentInfo))), + ('CreateDecoder', + com.STDMETHOD()), + ('CreateEncoder', + com.STDMETHOD()), + ('CreatePalette', + com.STDMETHOD()), + ('CreateFormatConverter', + com.STDMETHOD(POINTER(IWICFormatConverter))), + ('CreateBitmapScaler', + com.STDMETHOD()), + ('CreateBitmapClipper', + com.STDMETHOD()), + ('CreateBitmapFlipRotator', + com.STDMETHOD(POINTER(IWICBitmapFlipRotator))), + ('CreateStream', + com.STDMETHOD()), + ('CreateColorContext', + com.STDMETHOD()), + ('CreateColorTransformer', + com.STDMETHOD()), + ('CreateBitmap', + com.STDMETHOD(UINT, UINT, REFWICPixelFormatGUID, WICBitmapCreateCacheOption, POINTER(IWICBitmap))), + ('CreateBitmapFromSource', + com.STDMETHOD()), + ('CreateBitmapFromSourceRect', + com.STDMETHOD()), + ('CreateBitmapFromMemory', + com.STDMETHOD()), + ('CreateBitmapFromHBITMAP', + com.STDMETHOD()), + ('CreateBitmapFromHICON', + com.STDMETHOD()), + ('CreateComponentEnumerator', + com.STDMETHOD()), + ('CreateFastMetadataEncoderFromDecoder', + com.STDMETHOD()), + ('CreateFastMetadataEncoderFromFrameDecode', + com.STDMETHOD()), + ('CreateQueryWriter', + com.STDMETHOD()), + ('CreateQueryWriterFromReader', + com.STDMETHOD()) + ] + + +class WICDecoder(ImageDecoder): + """Windows Imaging Component. + This decoder is a replacement for GDI and GDI+ starting with Windows 7 with more features up to Windows 10.""" + def __init__(self): + super(ImageDecoder, self).__init__() + self._factory = IWICImagingFactory() + + try: + ole32.CoInitializeEx(None, COINIT_MULTITHREADED) + except OSError as err: + warnings.warn(str(err)) + + ole32.CoCreateInstance(CLSID_WICImagingFactory, + None, + CLSCTX_INPROC_SERVER, + IID_IWICImagingFactory, + byref(self._factory)) + + def get_file_extensions(self): + return ['.bmp', '.jpg', '.jpeg', '.png', '.tif', '.tiff', '.ico', '.jxr', '.hdp', '.wdp'] + + def _load_bitmap_decoder(self, file, filename): + data = file.read() + + # Create a HGLOBAL with image data + hglob = kernel32.GlobalAlloc(GMEM_MOVEABLE, len(data)) + ptr = kernel32.GlobalLock(hglob) + memmove(ptr, data, len(data)) + kernel32.GlobalUnlock(hglob) + + # Create IStream for the HGLOBAL + stream = com.pIUnknown() + ole32.CreateStreamOnHGlobal(hglob, True, byref(stream)) + + # Load image from stream + decoder = IWICBitmapDecoder() + status = self._factory.CreateDecoderFromStream(stream, None, WICDecodeMetadataCacheOnDemand, byref(decoder)) + if status != 0: + stream.Release() + raise ImageDecodeException('WIC cannot load %r' % (filename or file)) + + return decoder, stream + + def _get_bitmap_frame(self, bitmap_decoder, frame_index): + bitmap = IWICBitmapFrameDecode() + bitmap_decoder.GetFrame(frame_index, byref(bitmap)) + return bitmap + + def _get_image(self, bitmap, target_fmt=GUID_WICPixelFormat32bppBGRA): + """Get's image from bitmap, specifying target format, bitmap is released before returning.""" + width = UINT() + height = UINT() + + bitmap.GetSize(byref(width), byref(height)) + + width = int(width.value) + height = int(height.value) + + # Get image pixel format + pf = com.GUID(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + bitmap.GetPixelFormat(byref(pf)) + + fmt = 'BGRA' + # If target format is not what we want (32bit BGRA) convert it. + if pf != target_fmt: + converter = IWICFormatConverter() + self._factory.CreateFormatConverter(byref(converter)) + + conversion_possible = BOOL() + converter.CanConvert(pf, target_fmt, byref(conversion_possible)) + + # 99% of the time conversion will be possible to default. + # However, we check to be safe and fallback to 24 bit BGR if not possible. + if not conversion_possible: + target_fmt = GUID_WICPixelFormat24bppBGR + fmt = 'BGR' + + converter.Initialize(bitmap, target_fmt, WICBitmapDitherTypeNone, None, 0, WICBitmapPaletteTypeCustom) + + bitmap.Release() + bitmap = converter + + # Most images are loaded with a negative pitch, which requires list comprehension to fix. + # Create a flipped bitmap through the decoder rather through Python to increase performance. + flipper = IWICBitmapFlipRotator() + self._factory.CreateBitmapFlipRotator(byref(flipper)) + + flipper.Initialize(bitmap, WICBitmapTransformFlipVertical) + + stride = len(fmt) * width + buffer_size = stride * height + + buffer = (BYTE * buffer_size)() + + flipper.CopyPixels(None, stride, buffer_size, byref(buffer)) + + flipper.Release() + bitmap.Release() # Can be converter. + + return ImageData(width, height, fmt, buffer) + + def _delete_bitmap_decoder(self, bitmap_decoder, stream): + # Release decoder and stream + bitmap_decoder.Release() + stream.Release() + + def decode(self, file, filename): + bitmap_decoder, stream = self._load_bitmap_decoder(file, filename) + bitmap = self._get_bitmap_frame(bitmap_decoder, 0) + image = self._get_image(bitmap) + self._delete_bitmap_decoder(bitmap_decoder, stream) + return image + + @staticmethod + def get_property_value(reader, metadata_name): + """ + Uses a metadata name and reader to return a single value. Can be used to get metadata from images. + If failure, returns 0. + Also handles cleanup of PROPVARIANT. + """ + try: + prop = PROPVARIANT() + reader.GetMetadataByName(metadata_name, byref(prop)) + value = prop.llVal + ole32.PropVariantClear(byref(prop)) + except OSError: + value = 0 + + return value + + +def get_decoders(): + return [WICDecoder()] + + +def get_encoders(): + return [] diff -Nru pyglet-1.4.10/pyglet/image/__init__.py pyglet-1.5.14/pyglet/image/__init__.py --- pyglet-1.4.10/pyglet/image/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/image/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -128,24 +128,17 @@ use of the data in this arbitrary format). """ - -from __future__ import division -from builtins import bytes -from builtins import zip - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -from io import open import re import weakref from ctypes import * +from io import open, BytesIO +from functools import lru_cache from pyglet.gl import * from pyglet.gl import gl_info from pyglet.window import * -from pyglet.compat import asbytes, bytes_type, BytesIO +from pyglet.util import asbytes from .codecs import ImageEncodeException, ImageDecodeException from .codecs import add_default_image_codecs, add_decoders, add_encoders @@ -274,6 +267,7 @@ return pattern.create_image(width, height) +@lru_cache() def get_max_texture_size(): """Query the maximum texture size available""" size = c_int() @@ -281,15 +275,14 @@ return size.value +@lru_cache() def _color_as_bytes(color): - if sys.version.startswith('2'): - return '%c%c%c%c' % color - else: - if len(color) != 4: - raise TypeError("color is expected to have 4 components") - return bytes(color) + if len(color) != 4: + raise TypeError("color is expected to have 4 components") + return bytes(color) +@lru_cache() def _nearest_pow2(v): # From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 # Credit: Sean Anderson @@ -302,12 +295,13 @@ return v + 1 +@lru_cache() def _is_pow2(v): # http://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 return (v & (v - 1)) == 0 -class ImagePattern(object): +class ImagePattern: """Abstract image creation class.""" def create_image(self, width, height): @@ -373,7 +367,7 @@ return ImageData(width, height, 'RGBA', data) -class AbstractImage(object): +class AbstractImage: """Abstract class representing an image. :Parameters: @@ -545,7 +539,7 @@ raise ImageException('Cannot blit %r to a texture.' % self) -class AbstractImageSequence(object): +class AbstractImageSequence: """Abstract sequence of images. The sequence is useful for storing image animations or slices of a volume. @@ -1088,7 +1082,7 @@ return asbytes(data) def _ensure_string_data(self): - if type(self._current_data) is not bytes_type: + if type(self._current_data) is not bytes: buf = create_string_buffer(len(self._current_data)) memmove(buf, self._current_data, len(self._current_data)) self._current_data = buf.raw @@ -1894,7 +1888,7 @@ source.blit_to_texture(self.level, x, y, z) -class BufferManager(object): +class BufferManager: """Manages the set of framebuffers for a context. Use :py:func:`~pyglet.image.get_buffer_manager` to obtain the instance of this class for the diff -Nru pyglet-1.4.10/pyglet/info.py pyglet-1.5.14/pyglet/info.py --- pyglet-1.4.10/pyglet/info.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/info.py 2020-12-23 15:47:40.000000000 +0000 @@ -40,10 +40,6 @@ python -m pyglet.info > info.txt """ -from __future__ import print_function - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' _first_heading = True diff -Nru pyglet-1.4.10/pyglet/__init__.py pyglet-1.5.14/pyglet/__init__.py --- pyglet-1.4.10/pyglet/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/__init__.py 2020-12-31 21:10:58.000000000 +0000 @@ -37,35 +37,13 @@ Detailed documentation is available at http://www.pyglet.org """ -from __future__ import print_function -from __future__ import absolute_import - -# Check if future is installed, if not use included batteries -try: - import future -except ImportError: - import os.path as op - import sys - - future_base = op.abspath(op.join(op.dirname(__file__), 'extlibs', 'future')) - sys.path.insert(0, op.join(future_base, 'py2_3')) - if sys.version_info[:2] < (3, 0): - sys.path.insert(0, op.join(future_base, 'py2')) - del future_base - del sys - del op - try: - import future - except ImportError: - print('Failed to get python-future') - raise - -from builtins import range -from builtins import object import os import sys +from typing import TYPE_CHECKING + + if 'sphinx' in sys.modules: setattr(sys, 'is_pyglet_doc_run', True) _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run @@ -83,9 +61,14 @@ #: >>> parse_version(pyglet.version) >= parse_version('1.1') #: True #: -version = '1.4.10' +version = '1.5.14' + + +if sys.version_info < (3, 5): + raise Exception('pyglet %s requires Python 3.5 or newer.' % version) -# Pyglet platform treats *BSD systems as Linux + +# pyglet platform treats *BSD systems as Linux compat_platform = sys.platform if "bsd" in compat_platform: compat_platform = "linux-compat" @@ -114,6 +97,7 @@ #: A sequence of the names of audio modules to attempt to load, in #: order of preference. Valid driver names are: #: +#: * xaudio2, the Windows Xaudio2 audio module (Windows only) #: * directsound, the Windows DirectSound audio module (Windows only) #: * pulse, the PulseAudio module (Linux only) #: * openal, the OpenAL audio module @@ -164,7 +148,7 @@ #: .. versionadded:: 1.2 #: options = { - 'audio': ('directsound', 'openal', 'pulse', 'silent'), + 'audio': ('xaudio2', 'directsound', 'openal', 'pulse', 'silent'), 'debug_font': False, 'debug_gl': not _enable_optimisations, 'debug_gl_trace': False, @@ -342,7 +326,7 @@ # Lazy loading # ------------ -class _ModuleProxy(object): +class _ModuleProxy: _module = None def __init__(self, name): @@ -377,28 +361,9 @@ setattr(module, name, value) -if True: - app = _ModuleProxy('app') - canvas = _ModuleProxy('canvas') - clock = _ModuleProxy('clock') - com = _ModuleProxy('com') - event = _ModuleProxy('event') - font = _ModuleProxy('font') - gl = _ModuleProxy('gl') - graphics = _ModuleProxy('graphics') - image = _ModuleProxy('image') - input = _ModuleProxy('input') - lib = _ModuleProxy('lib') - media = _ModuleProxy('media') - model = _ModuleProxy('model') - resource = _ModuleProxy('resource') - sprite = _ModuleProxy('sprite') - text = _ModuleProxy('text') - window = _ModuleProxy('window') - -# Fool py2exe, py2app into including all top-level modules (doesn't understand -# lazy loading) -if False: +# Lazily load all modules, except if performing +# type checking or code inspection. +if TYPE_CHECKING: from . import app from . import canvas from . import clock @@ -407,17 +372,36 @@ from . import font from . import gl from . import graphics + from . import gui from . import input from . import image from . import lib + from . import math from . import media from . import model from . import resource from . import sprite + from . import shapes from . import text from . import window - -# Hack around some epydoc bug that causes it to think pyglet.window is None. -# TODO: confirm if this is still needed -if False: - from . import window +else: + app = _ModuleProxy('app') + canvas = _ModuleProxy('canvas') + clock = _ModuleProxy('clock') + com = _ModuleProxy('com') + event = _ModuleProxy('event') + font = _ModuleProxy('font') + gl = _ModuleProxy('gl') + graphics = _ModuleProxy('graphics') + gui = _ModuleProxy('gui') + image = _ModuleProxy('image') + input = _ModuleProxy('input') + lib = _ModuleProxy('lib') + math = _ModuleProxy('math') + media = _ModuleProxy('media') + model = _ModuleProxy('model') + resource = _ModuleProxy('resource') + sprite = _ModuleProxy('sprite') + shapes = _ModuleProxy('shapes') + text = _ModuleProxy('text') + window = _ModuleProxy('window') diff -Nru pyglet-1.4.10/pyglet/input/base.py pyglet-1.5.14/pyglet/input/base.py --- pyglet-1.4.10/pyglet/input/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/base.py 2020-12-23 15:47:40.000000000 +0000 @@ -37,13 +37,9 @@ .. versionadded:: 1.2 """ -from __future__ import division -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import sys + from pyglet.event import EventDispatcher _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run @@ -61,7 +57,7 @@ pass -class Device(object): +class Device: """Input device. :Ivariables: @@ -616,7 +612,7 @@ AppleRemote.register_event_type('on_button_release') -class Tablet(object): +class Tablet: """High-level interface to tablet devices. Unlike other devices, tablets must be opened for a specific window, @@ -731,7 +727,7 @@ TabletCanvas.register_event_type('on_motion') -class TabletCursor(object): +class TabletCursor: """A distinct cursor used on a tablet. Most tablets support at least a *stylus* and an *erasor* cursor; this diff -Nru pyglet-1.4.10/pyglet/input/darwin_hid.py pyglet-1.5.14/pyglet/input/darwin_hid.py --- pyglet-1.4.10/pyglet/input/darwin_hid.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/darwin_hid.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,30 +32,25 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import print_function -from __future__ import absolute_import -from builtins import object import sys -from ctypes import cdll, util, CFUNCTYPE, byref, c_void_p -from ctypes import c_int, c_ubyte, c_bool, c_uint32, c_uint64 +from ctypes import CFUNCTYPE, byref, c_void_p, c_int, c_ubyte, c_bool, c_uint32, c_uint64 -from .base import Device, Control, AbsoluteAxis, RelativeAxis, Button +from .base import Device, AbsoluteAxis, RelativeAxis, Button from .base import Joystick, AppleRemote -from .base import DeviceExclusiveException from pyglet.libs.darwin.cocoapy import CFSTR, CFIndex, CFTypeID, known_cftypes from pyglet.libs.darwin.cocoapy import kCFRunLoopDefaultMode, CFAllocatorRef, cf from pyglet.libs.darwin.cocoapy import cfset_to_set, cftype_to_value, cfarray_to_list +from pyglet.lib import load_library + __LP64__ = (sys.maxsize > 2 ** 32) # Uses the HID API introduced in Mac OS X version 10.5 # http://developer.apple.com/library/mac/#technotes/tn2007/tn2187.html - -# Load iokit framework -iokit = cdll.LoadLibrary(util.find_library('IOKit')) +iokit = load_library(framework='IOKit') # IOKit constants from # /System/Library/Frameworks/IOKit.framework/Headers/hid/IOHIDKeys.h @@ -249,7 +244,7 @@ _element_lookup = {} # IOHIDElementRef to python HIDDeviceElement object -class HIDValue(object): +class HIDValue: def __init__(self, valueRef): # Check that this is a valid IOHIDValue. assert valueRef @@ -268,7 +263,7 @@ self.element = HIDDeviceElement.get_element(elementRef) -class HIDDevice(object): +class HIDDevice: @classmethod def get_device(cls, deviceRef): # deviceRef is a c_void_p pointing to an IOHIDDeviceRef @@ -388,9 +383,9 @@ # Remove self from device lookup table. del _device_lookup[sender] # Remove device elements from lookup table. - for key, value in _element_lookup.items(): - if value in self.elements: - del _element_lookup[key] + to_remove = [k for k, v in _element_lookup.items() if v in self.elements] + for key in to_remove: + del _element_lookup[key] def _register_removal_callback(self): removal_callback = HIDDeviceCallback(self.py_removal_callback) @@ -425,7 +420,7 @@ return None -class HIDDeviceElement(object): +class HIDDeviceElement: @classmethod def get_element(cls, elementRef): # elementRef is a c_void_p pointing to an IOHIDDeviceElementRef @@ -469,7 +464,7 @@ self.physicalMax = iokit.IOHIDElementGetPhysicalMax(elementRef) -class HIDManager(object): +class HIDManager: def __init__(self): # Create the HID Manager. self.managerRef = c_void_p(iokit.IOHIDManagerCreate(None, kIOHIDOptionsTypeNone)) diff -Nru pyglet-1.4.10/pyglet/input/directinput.py pyglet-1.5.14/pyglet/input/directinput.py --- pyglet-1.4.10/pyglet/input/directinput.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/directinput.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,9 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from builtins import zip -#!/usr/bin/python -# $Id:$ import ctypes @@ -65,6 +62,7 @@ _btn_instance_names = {} + def _create_control(object_instance): raw_name = object_instance.tszName type = object_instance.dwType @@ -87,7 +85,8 @@ control._type = object_instance.dwType return control - + + class DirectInputDevice(base.Device): def __init__(self, display, device, device_instance): name = device_instance.tszInstanceName @@ -139,8 +138,7 @@ prop.diph.dwObj = 0 prop.diph.dwHow = dinput.DIPH_DEVICE prop.dwData = 64 * ctypes.sizeof(dinput.DIDATAFORMAT) - self._device.SetProperty(dinput.DIPROP_BUFFERSIZE, - ctypes.byref(prop.diph)) + self._device.SetProperty(dinput.DIPROP_BUFFERSIZE, ctypes.byref(prop.diph)) def open(self, window=None, exclusive=False): if not self.controls: @@ -161,8 +159,7 @@ self._wait_object = _kernel32.CreateEventW(None, False, False, None) self._device.SetEventNotification(self._wait_object) - pyglet.app.platform_event_loop.add_wait_object(self._wait_object, - self._dispatch_events) + pyglet.app.platform_event_loop.add_wait_object(self._wait_object, self._dispatch_events) self._device.SetCooperativeLevel(window._hwnd, flags) self._device.Acquire() @@ -187,17 +184,23 @@ events = (dinput.DIDEVICEOBJECTDATA * 64)() n_events = win32.DWORD(len(events)) - self._device.GetDeviceData(ctypes.sizeof(dinput.DIDEVICEOBJECTDATA), - ctypes.cast(ctypes.pointer(events), - dinput.LPDIDEVICEOBJECTDATA), - ctypes.byref(n_events), - 0) + try: + self._device.GetDeviceData(ctypes.sizeof(dinput.DIDEVICEOBJECTDATA), + ctypes.cast(ctypes.pointer(events), + dinput.LPDIDEVICEOBJECTDATA), + ctypes.byref(n_events), + 0) + except OSError: + return + for event in events[:n_events.value]: index = event.dwOfs // 4 self.controls[index].value = event.dwData + _i_dinput = None + def _init_directinput(): global _i_dinput if _i_dinput: @@ -209,6 +212,7 @@ dinput.IID_IDirectInput8W, ctypes.byref(_i_dinput), None) + def get_devices(display=None): _init_directinput() _devices = [] @@ -228,12 +232,14 @@ None, dinput.DIEDFL_ATTACHEDONLY) return _devices + def _create_joystick(device): if device._type in (dinput.DI8DEVTYPE_JOYSTICK, dinput.DI8DEVTYPE_1STPERSON, dinput.DI8DEVTYPE_GAMEPAD): return base.Joystick(device) + def get_joysticks(display=None): return [joystick for joystick diff -Nru pyglet-1.4.10/pyglet/input/evdev_constants.py pyglet-1.5.14/pyglet/input/evdev_constants.py --- pyglet-1.4.10/pyglet/input/evdev_constants.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/evdev_constants.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -36,9 +35,6 @@ """Event constants from /usr/include/linux/input.h """ -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - EV_SYN = 0x00 EV_KEY = 0x01 diff -Nru pyglet-1.4.10/pyglet/input/evdev.py pyglet-1.5.14/pyglet/input/evdev.py --- pyglet-1.4.10/pyglet/input/evdev.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/evdev.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -34,26 +33,17 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from __future__ import absolute_import -from builtins import hex -from builtins import range - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -import ctypes -import errno import os +import errno +import ctypes import pyglet + from pyglet.app.xlib import XlibSelectDevice from .base import Device, Control, RelativeAxis, AbsoluteAxis, Button, Joystick from .base import DeviceOpenException from .evdev_constants import * - c = pyglet.lib.load_library('c') _IOC_NRBITS = 8 @@ -61,36 +51,41 @@ _IOC_SIZEBITS = 14 _IOC_DIRBITS = 2 -_IOC_NRMASK = ((1 << _IOC_NRBITS)-1) -_IOC_TYPEMASK = ((1 << _IOC_TYPEBITS)-1) -_IOC_SIZEMASK = ((1 << _IOC_SIZEBITS)-1) -_IOC_DIRMASK = ((1 << _IOC_DIRBITS)-1) +_IOC_NRMASK = ((1 << _IOC_NRBITS) - 1) +_IOC_TYPEMASK = ((1 << _IOC_TYPEBITS) - 1) +_IOC_SIZEMASK = ((1 << _IOC_SIZEBITS) - 1) +_IOC_DIRMASK = ((1 << _IOC_DIRBITS) - 1) _IOC_NRSHIFT = 0 -_IOC_TYPESHIFT = (_IOC_NRSHIFT+_IOC_NRBITS) -_IOC_SIZESHIFT = (_IOC_TYPESHIFT+_IOC_TYPEBITS) -_IOC_DIRSHIFT = (_IOC_SIZESHIFT+_IOC_SIZEBITS) +_IOC_TYPESHIFT = (_IOC_NRSHIFT + _IOC_NRBITS) +_IOC_SIZESHIFT = (_IOC_TYPESHIFT + _IOC_TYPEBITS) +_IOC_DIRSHIFT = (_IOC_SIZESHIFT + _IOC_SIZEBITS) _IOC_NONE = 0 _IOC_WRITE = 1 _IOC_READ = 2 + def _IOC(dir, type, nr, size): return ((dir << _IOC_DIRSHIFT) | (type << _IOC_TYPESHIFT) | (nr << _IOC_NRSHIFT) | (size << _IOC_SIZESHIFT)) + def _IOR(type, nr, struct): request = _IOC(_IOC_READ, ord(type), nr, ctypes.sizeof(struct)) + def f(fileno): buffer = struct() if c.ioctl(fileno, request, ctypes.byref(buffer)) < 0: err = ctypes.c_int.in_dll(c, 'errno').value raise OSError(err, errno.errorcode[err]) return buffer + return f + def _IOR_len(type, nr): def f(fileno, buffer): request = _IOC(_IOC_READ, ord(type), nr, ctypes.sizeof(buffer)) @@ -98,23 +93,30 @@ err = ctypes.c_int.in_dll(c, 'errno').value raise OSError(err, errno.errorcode[err]) return buffer + return f + def _IOR_str(type, nr): g = _IOR_len(type, nr) + def f(fileno, len=256): return g(fileno, ctypes.create_string_buffer(len)).value + return f + time_t = ctypes.c_long suseconds_t = ctypes.c_long + class timeval(ctypes.Structure): _fields_ = ( ('tv_sec', time_t), ('tv_usec', suseconds_t) ) + class input_event(ctypes.Structure): _fields_ = ( ('time', timeval), @@ -123,6 +125,7 @@ ('value', ctypes.c_int32) ) + class input_id(ctypes.Structure): _fields_ = ( ('bustype', ctypes.c_uint16), @@ -131,6 +134,7 @@ ('version', ctypes.c_uint16), ) + class input_absinfo(ctypes.Structure): _fields_ = ( ('value', ctypes.c_int32), @@ -140,17 +144,23 @@ ('flat', ctypes.c_int32), ) + EVIOCGVERSION = _IOR('E', 0x01, ctypes.c_int) EVIOCGID = _IOR('E', 0x02, input_id) EVIOCGNAME = _IOR_str('E', 0x06) EVIOCGPHYS = _IOR_str('E', 0x07) EVIOCGUNIQ = _IOR_str('E', 0x08) + + def EVIOCGBIT(fileno, ev, buffer): return _IOR_len('E', 0x20 + ev)(fileno, buffer) + + def EVIOCGABS(fileno, abs): buffer = input_absinfo() return _IOR_len('E', 0x40 + abs)(fileno, buffer) + def get_set_bits(bytes): bits = set() j = 0 @@ -162,6 +172,7 @@ j += 8 return bits + _abs_names = { ABS_X: AbsoluteAxis.X, ABS_Y: AbsoluteAxis.Y, @@ -182,6 +193,8 @@ REL_RZ: RelativeAxis.RZ, REL_WHEEL: RelativeAxis.WHEEL, } + + def _create_control(fileno, event_type, event_code): if event_type == EV_ABS: raw_name = abs_raw_names.get(event_code, 'EV_ABS(%x)' % event_code) @@ -205,12 +218,13 @@ name = None control = Button(name, raw_name) else: - value = min = max = 0 # TODO + value = min = max = 0 # TODO return None control._event_type = event_type control._event_code = event_code return control + def _create_joystick(device): # Look for something with an ABS X and ABS Y axis, and a joystick 0 button have_x = False @@ -222,13 +236,14 @@ elif control._event_type == EV_ABS and control._event_code == ABS_Y: have_y = True elif control._event_type == EV_KEY and \ - control._event_code in (BTN_JOYSTICK, BTN_GAMEPAD): + control._event_code in (BTN_JOYSTICK, BTN_GAMEPAD): have_button = True if not (have_x and have_y and have_button): return return Joystick(device) + event_types = { EV_KEY: KEY_MAX, EV_REL: REL_MAX, @@ -238,14 +253,15 @@ EV_SND: SND_MAX, } + class EvdevDevice(XlibSelectDevice, Device): _fileno = None - + def __init__(self, display, filename): self._filename = filename fileno = os.open(filename, os.O_RDONLY) - #event_version = EVIOCGVERSION(fileno).value + # event_version = EVIOCGVERSION(fileno).value id = EVIOCGID(fileno) self.id_bustype = id.bustype @@ -261,7 +277,7 @@ name = name.decode('latin-1') except UnicodeDecodeError: pass - + try: self.phys = EVIOCGPHYS(fileno) except OSError: @@ -286,7 +302,7 @@ for event_code in get_set_bits(event_codes_bits): control = _create_control(fileno, event_type, event_code) if control: - self.control_map[(event_type, event_code)] = control + self.control_map[(event_type, event_code)] = control self.controls.append(control) os.close(fileno) @@ -342,7 +358,10 @@ except KeyError: pass + _devices = {} + + def get_devices(display=None): base = '/dev/input' for filename in os.listdir(base): @@ -354,10 +373,11 @@ try: _devices[path] = EvdevDevice(display, path) except OSError: - pass + pass return list(_devices.values()) + def get_joysticks(display=None): return [joystick for joystick diff -Nru pyglet-1.4.10/pyglet/input/__init__.py pyglet-1.5.14/pyglet/input/__init__.py --- pyglet-1.4.10/pyglet/input/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -80,10 +80,6 @@ .. versionadded:: 1.2 """ -from __future__ import absolute_import - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import sys diff -Nru pyglet-1.4.10/pyglet/input/wintab.py pyglet-1.5.14/pyglet/input/wintab.py --- pyglet-1.4.10/pyglet/input/wintab.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/wintab.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -33,11 +32,7 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ -from __future__ import print_function -from builtins import range -from builtins import object import ctypes import pyglet @@ -45,45 +40,54 @@ from pyglet.input.base import Tablet, TabletCursor, TabletCanvas from pyglet.libs.win32 import libwintab as wintab + lib = wintab.lib + def wtinfo(category, index, buffer): size = lib.WTInfoW(category, index, None) assert size <= ctypes.sizeof(buffer) lib.WTInfoW(category, index, ctypes.byref(buffer)) return buffer + def wtinfo_string(category, index): size = lib.WTInfoW(category, index, None) buffer = ctypes.create_unicode_buffer(size) lib.WTInfoW(category, index, buffer) return buffer.value + def wtinfo_uint(category, index): buffer = wintab.UINT() lib.WTInfoW(category, index, ctypes.byref(buffer)) return buffer.value + def wtinfo_word(category, index): buffer = wintab.WORD() lib.WTInfoW(category, index, ctypes.byref(buffer)) - return buffer.value + return buffer.value + def wtinfo_dword(category, index): buffer = wintab.DWORD() lib.WTInfoW(category, index, ctypes.byref(buffer)) - return buffer.value + return buffer.value + def wtinfo_wtpkt(category, index): buffer = wintab.WTPKT() lib.WTInfoW(category, index, ctypes.byref(buffer)) - return buffer.value + return buffer.value + def wtinfo_bool(category, index): buffer = wintab.BOOL() lib.WTInfoW(category, index, ctypes.byref(buffer)) return bool(buffer.value) + class WintabTablet(Tablet): def __init__(self, index): self._device = wintab.WTI_DEVICES + index @@ -91,13 +95,12 @@ self.id = wtinfo_string(self._device, wintab.DVC_PNPID) hardware = wtinfo_uint(self._device, wintab.DVC_HARDWARE) - #phys_cursors = hardware & wintab.HWC_PHYSID_CURSORS - + # phys_cursors = hardware & wintab.HWC_PHYSID_CURSORS + n_cursors = wtinfo_uint(self._device, wintab.DVC_NCSRTYPES) first_cursor = wtinfo_uint(self._device, wintab.DVC_FIRSTCSR) - self.pressure_axis = wtinfo(self._device, wintab.DVC_NPRESSURE, - wintab.AXIS()) + self.pressure_axis = wtinfo(self._device, wintab.DVC_NPRESSURE, wintab.AXIS()) self.cursors = [] self._cursor_map = {} @@ -111,6 +114,7 @@ def open(self, window): return WintabTabletCanvas(self, window) + class WintabTabletCanvas(TabletCanvas): def __init__(self, device, window, msg_base=wintab.WT_DEFBASE): super(WintabTabletCanvas, self).__init__(window) @@ -128,21 +132,18 @@ # If you change this, change definition of PACKET also. context_info.lcPktData = ( - wintab.PK_CHANGED | wintab.PK_CURSOR | wintab.PK_BUTTONS | - wintab.PK_X | wintab.PK_Y | wintab.PK_Z | - wintab.PK_NORMAL_PRESSURE | wintab.PK_TANGENT_PRESSURE | - wintab.PK_ORIENTATION) - context_info.lcPktMode = 0 # All absolute + wintab.PK_CHANGED | wintab.PK_CURSOR | wintab.PK_BUTTONS | + wintab.PK_X | wintab.PK_Y | wintab.PK_Z | + wintab.PK_NORMAL_PRESSURE | wintab.PK_TANGENT_PRESSURE | + wintab.PK_ORIENTATION) + context_info.lcPktMode = 0 # All absolute - self._context = lib.WTOpenW(window._hwnd, - ctypes.byref(context_info), True) + self._context = lib.WTOpenW(window._hwnd, ctypes.byref(context_info), True) if not self._context: raise DeviceOpenException("Couldn't open tablet context") - window._event_handlers[msg_base + wintab.WT_PACKET] = \ - self._event_wt_packet - window._event_handlers[msg_base + wintab.WT_PROXIMITY] = \ - self._event_wt_proximity + window._event_handlers[msg_base + wintab.WT_PACKET] = self._event_wt_packet + window._event_handlers[msg_base + wintab.WT_PROXIMITY] = self._event_wt_proximity self._current_cursor = None self._pressure_scale = device.pressure_axis.get_scale() @@ -176,18 +177,16 @@ if not packet.pkChanged: return - window_x, window_y = self.window.get_location() # TODO cache on window + window_x, window_y = self.window.get_location() # TODO cache on window window_y = self.window.screen.height - window_y - self.window.height x = packet.pkX - window_x y = packet.pkY - window_y - pressure = (packet.pkNormalPressure + self._pressure_bias) * \ - self._pressure_scale - + pressure = (packet.pkNormalPressure + self._pressure_bias) * self._pressure_scale + if self._current_cursor is None: self._set_current_cursor(packet.pkCursor) - self.dispatch_event('on_motion', self._current_cursor, - x, y, pressure, 0., 0.) + self.dispatch_event('on_motion', self._current_cursor, x, y, pressure, 0., 0.) print(packet.pkButtons) @@ -207,8 +206,9 @@ # If going in, proximity event will be generated by next event, which # can actually grab a cursor id. self._current_cursor = None - -class WintabTabletCursor(object): + + +class WintabTabletCursor: def __init__(self, device, index): self.device = device self._cursor = wintab.WTI_CURSORS + index @@ -216,7 +216,7 @@ self.name = wtinfo_string(self._cursor, wintab.CSR_NAME).strip() self.active = wtinfo_bool(self._cursor, wintab.CSR_ACTIVE) pktdata = wtinfo_wtpkt(self._cursor, wintab.CSR_PKTDATA) - + # A whole bunch of cursors are reported by the driver, but most of # them are hogwash. Make sure a cursor has at least X and Y data # before adding it to the device. @@ -226,22 +226,26 @@ self.id = (wtinfo_dword(self._cursor, wintab.CSR_TYPE) << 32) | \ wtinfo_dword(self._cursor, wintab.CSR_PHYSID) - + def __repr__(self): return 'WintabCursor(%r)' % self.name + def get_spec_version(): spec_version = wtinfo_word(wintab.WTI_INTERFACE, wintab.IFC_SPECVERSION) return spec_version + def get_interface_name(): interface_name = wtinfo_string(wintab.WTI_INTERFACE, wintab.IFC_WINTABID) return interface_name + def get_implementation_version(): impl_version = wtinfo_word(wintab.WTI_INTERFACE, wintab.IFC_IMPLVERSION) return impl_version + def get_tablets(display=None): # Require spec version 1.1 or greater if get_spec_version() < 0x101: diff -Nru pyglet-1.4.10/pyglet/input/x11_xinput.py pyglet-1.5.14/pyglet/input/x11_xinput.py --- pyglet-1.4.10/pyglet/input/x11_xinput.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/x11_xinput.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -34,33 +33,27 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from builtins import range -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - import ctypes import pyglet -from pyglet.input.base import \ - Device, DeviceException, DeviceOpenException, \ - Control, Button, RelativeAxis, AbsoluteAxis +from pyglet.input.base import Device, DeviceException, DeviceOpenException +from pyglet.input.base import Control, Button, RelativeAxis, AbsoluteAxis from pyglet.libs.x11 import xlib -from pyglet.compat import asstr +from pyglet.util import asstr try: from pyglet.libs.x11 import xinput as xi + _have_xinput = True except: _have_xinput = False + def ptr_add(ptr, offset): address = ctypes.addressof(ptr.contents) + offset return ctypes.pointer(type(ptr.contents).from_address(address)) -class DeviceResponder(object): + +class DeviceResponder: def _key_press(self, e): pass @@ -82,6 +75,7 @@ def _proximity_out(self, e): pass + class XInputDevice(DeviceResponder, Device): def __init__(self, display, device_info): super(XInputDevice, self).__init__(display, asstr(device_info.name)) @@ -110,7 +104,7 @@ cp = ctypes.cast(ptr, ctypes.POINTER(xi.XButtonInfo)) num_buttons = cp.contents.num_buttons # Pointer buttons start at index 1, with 0 as 'AnyButton' - for i in range(num_buttons+1): + for i in range(num_buttons + 1): self.buttons.append(Button('button%d' % i)) elif cls_class == xi.ValuatorClass: @@ -123,8 +117,8 @@ axis = axes[i] if mode == xi.Absolute: self.axes.append(AbsoluteAxis('axis%d' % i, - min=axis.min_value, - max=axis.max_value)) + min=axis.min_value, + max=axis.max_value)) elif mode == xi.Relative: self.axes.append(RelativeAxis('axis%d' % i)) @@ -165,7 +159,7 @@ if not self._device: self.is_open = False raise DeviceOpenException('Cannot open device') - + self._install_events(window) def close(self): @@ -207,7 +201,8 @@ if self.proximity_control: self.proximity_control.value = False -class XInputWindowEventDispatcher(object): + +class XInputWindowEventDispatcher: def __init__(self, window): self.window = window self._responders = {} @@ -279,7 +274,7 @@ elif class_info.input_class == xi.FocusClass: pass - + elif class_info.input_class == xi.OtherClass: pass @@ -292,7 +287,7 @@ @pyglet.window.xlib.XlibEventHandler(0) def _event_xinput_key_press(self, ev): e = ctypes.cast(ctypes.byref(ev), - ctypes.POINTER(xi.XDeviceKeyEvent)).contents + ctypes.POINTER(xi.XDeviceKeyEvent)).contents device = self._responders.get(e.deviceid) if device is not None: @@ -301,7 +296,7 @@ @pyglet.window.xlib.XlibEventHandler(0) def _event_xinput_key_release(self, ev): e = ctypes.cast(ctypes.byref(ev), - ctypes.POINTER(xi.XDeviceKeyEvent)).contents + ctypes.POINTER(xi.XDeviceKeyEvent)).contents device = self._responders.get(e.deviceid) if device is not None: @@ -310,7 +305,7 @@ @pyglet.window.xlib.XlibEventHandler(0) def _event_xinput_button_press(self, ev): e = ctypes.cast(ctypes.byref(ev), - ctypes.POINTER(xi.XDeviceButtonEvent)).contents + ctypes.POINTER(xi.XDeviceButtonEvent)).contents device = self._responders.get(e.deviceid) if device is not None: @@ -319,7 +314,7 @@ @pyglet.window.xlib.XlibEventHandler(0) def _event_xinput_button_release(self, ev): e = ctypes.cast(ctypes.byref(ev), - ctypes.POINTER(xi.XDeviceButtonEvent)).contents + ctypes.POINTER(xi.XDeviceButtonEvent)).contents device = self._responders.get(e.deviceid) if device is not None: @@ -328,7 +323,7 @@ @pyglet.window.xlib.XlibEventHandler(0) def _event_xinput_motion(self, ev): e = ctypes.cast(ctypes.byref(ev), - ctypes.POINTER(xi.XDeviceMotionEvent)).contents + ctypes.POINTER(xi.XDeviceMotionEvent)).contents device = self._responders.get(e.deviceid) if device is not None: @@ -337,7 +332,7 @@ @pyglet.window.xlib.XlibEventHandler(0) def _event_xinput_proximity_in(self, ev): e = ctypes.cast(ctypes.byref(ev), - ctypes.POINTER(xi.XProximityNotifyEvent)).contents + ctypes.POINTER(xi.XProximityNotifyEvent)).contents device = self._responders.get(e.deviceid) if device is not None: @@ -346,12 +341,13 @@ @pyglet.window.xlib.XlibEventHandler(-1) def _event_xinput_proximity_out(self, ev): e = ctypes.cast(ctypes.byref(ev), - ctypes.POINTER(xi.XProximityNotifyEvent)).contents + ctypes.POINTER(xi.XProximityNotifyEvent)).contents device = self._responders.get(e.deviceid) if device is not None: device._proximity_out(e) + def _check_extension(display): major_opcode = ctypes.c_int() first_event = ctypes.c_int() @@ -362,6 +358,7 @@ ctypes.byref(first_error)) return bool(major_opcode.value) + def get_devices(display=None): if display is None: display = pyglet.canvas.get_display() @@ -380,4 +377,3 @@ xi.XFreeDeviceList(device_list) return devices - diff -Nru pyglet-1.4.10/pyglet/input/x11_xinput_tablet.py pyglet-1.5.14/pyglet/input/x11_xinput_tablet.py --- pyglet-1.4.10/pyglet/input/x11_xinput_tablet.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/input/x11_xinput_tablet.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/env python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -34,13 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from __future__ import division - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - from pyglet.input.base import Tablet, TabletCanvas from pyglet.input.base import TabletCursor, DeviceOpenException from pyglet.input.x11_xinput import XInputWindowEventDispatcher diff -Nru pyglet-1.4.10/pyglet/lib.py pyglet-1.5.14/pyglet/lib.py --- pyglet-1.4.10/pyglet/lib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/lib.py 2020-12-31 21:03:32.000000000 +0000 @@ -36,11 +36,6 @@ These extend and correct ctypes functions. """ -from __future__ import print_function -from builtins import object, str - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import os import re @@ -54,7 +49,7 @@ _debug_lib = pyglet.options['debug_lib'] _debug_trace = pyglet.options['debug_trace'] -_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run +_is_pyglet_doc_run = getattr(sys, "is_pyglet_doc_run", False) if pyglet.options['search_local_libs']: script_path = pyglet.resource.get_script_home() @@ -66,7 +61,7 @@ _local_lib_paths = None -class _TraceFunction(object): +class _TraceFunction: def __init__(self, func): self.__dict__['_func'] = func @@ -83,7 +78,7 @@ setattr(self._func, name, value) -class _TraceLibrary(object): +class _TraceLibrary: def __init__(self, library): self._library = library print(library) @@ -95,7 +90,7 @@ if _is_pyglet_doc_run: - class LibraryMock(object): + class LibraryMock: """Mock library used when generating documentation.""" def __getattr__(self, name): return LibraryMock() @@ -107,7 +102,7 @@ return LibraryMock() -class LibraryLoader(object): +class LibraryLoader: platform = pyglet.compat_platform # this is only for library loading, don't include it in pyglet.platform @@ -171,7 +166,8 @@ def find_library(self, name): return ctypes.util.find_library(name) - def load_framework(self, path): + @staticmethod + def load_framework(name): raise RuntimeError("Can't load framework on this platform.") @@ -195,10 +191,8 @@ if 'DYLD_FALLBACK_LIBRARY_PATH' in os.environ: self.dyld_fallback_library_path = os.environ['DYLD_FALLBACK_LIBRARY_PATH'].split(':') else: - self.dyld_fallback_library_path = [os.path.expanduser('~/lib'), - '/usr/local/lib', - '/usr/lib'] - + self.dyld_fallback_library_path = [os.path.expanduser('~/lib'), '/usr/local/lib', '/usr/lib'] + def find_library(self, path): """Implements the dylib search as specified in Apple documentation: @@ -249,39 +243,28 @@ return None @staticmethod - def find_framework(path): - """Implement runtime framework search as described by: + def load_framework(name): + path = ctypes.util.find_library(name) - http://developer.apple.com/library/content/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkBinding.html - """ - - # e.g. path == '/System/Library/Frameworks/OpenGL.framework' - # name == 'OpenGL' - # return '/System/Library/Frameworks/OpenGL.framework/OpenGL' - name = os.path.splitext(os.path.split(path)[1])[0] - - realpath = os.path.join(path, name) - if os.path.exists(realpath): - return realpath - - for directory in ('/Library/Frameworks', '/System/Library/Frameworks'): - realpath = os.path.join(directory, '%s.framework' % name, name) - if os.path.exists(realpath): - return realpath - - return None + # Hack for compatibility with macOS > 11.0 + if path is None: + frameworks = { + 'AGL': '/System/Library/Frameworks/AGL.framework/AGL', + 'IOKit': '/System/Library/Frameworks/IOKit.framework/IOKit', + 'OpenAL': '/System/Library/Frameworks/OpenAL.framework/OpenAL', + 'OpenGL': '/System/Library/Frameworks/OpenGL.framework/OpenGL' + } + path = frameworks.get(name) - def load_framework(self, path): - realpath = self.find_framework(path) - if realpath: - lib = ctypes.cdll.LoadLibrary(realpath) + if path: + lib = ctypes.cdll.LoadLibrary(path) if _debug_lib: - print(realpath) + print(path) if _debug_trace: lib = _TraceLibrary(lib) return lib - raise ImportError("Can't find framework %s." % path) + raise ImportError("Can't find framework %s." % name) class LinuxLibraryLoader(LibraryLoader): diff -Nru pyglet-1.4.10/pyglet/libs/darwin/cocoapy/cocoalibs.py pyglet-1.5.14/pyglet/libs/darwin/cocoapy/cocoalibs.py --- pyglet-1.4.10/pyglet/libs/darwin/cocoapy/cocoalibs.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/darwin/cocoapy/cocoalibs.py 2020-12-31 21:03:32.000000000 +0000 @@ -1,5 +1,3 @@ -from builtins import str - from ctypes import * from ctypes import util @@ -9,8 +7,13 @@ ###################################################################### # CORE FOUNDATION +lib = util.find_library('CoreFoundation') + +# Hack for compatibility with macOS > 11.0 +if lib is None: + lib = '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation' -cf = cdll.LoadLibrary(util.find_library('CoreFoundation')) +cf = cdll.LoadLibrary(lib) kCFStringEncodingUTF8 = 0x08000100 @@ -194,7 +197,13 @@ # Even though we don't use this directly, it must be loaded so that # we can find the NSApplication, NSWindow, and NSView classes. -appkit = cdll.LoadLibrary(util.find_library('AppKit')) +lib = util.find_library('AppKit') + +# Hack for compatibility with macOS > 11.0 +if lib is None: + lib = '/System/Library/Frameworks/AppKit.framework/AppKit' + +appkit = cdll.LoadLibrary(lib) NSDefaultRunLoopMode = c_void_p.in_dll(appkit, 'NSDefaultRunLoopMode') NSEventTrackingRunLoopMode = c_void_p.in_dll(appkit, 'NSEventTrackingRunLoopMode') @@ -332,8 +341,13 @@ ###################################################################### # QUARTZ / COREGRAPHICS +lib = util.find_library('Quartz') -quartz = cdll.LoadLibrary(util.find_library('quartz')) +# Hack for compatibility with macOS > 11.0 +if lib is None: + lib = '/System/Library/Frameworks/Quartz.framework/Quartz' + +quartz = cdll.LoadLibrary(lib) CGDirectDisplayID = c_uint32 # CGDirectDisplay.h CGError = c_int32 # CGError.h @@ -482,7 +496,13 @@ ###################################################################### # CORETEXT -ct = cdll.LoadLibrary(util.find_library('CoreText')) +lib = util.find_library('CoreText') + +# Hack for compatibility with macOS > 11.0 +if lib is None: + lib = '/System/Library/Frameworks/CoreText.framework/CoreText' + +ct = cdll.LoadLibrary(lib) # Types CTFontOrientation = c_uint32 # CTFontDescriptor.h @@ -541,8 +561,13 @@ ###################################################################### # FOUNDATION +lib = util.find_library('Foundation') + +# Hack for compatibility with macOS > 11.0 +if lib is None: + lib = '/System/Library/Frameworks/Foundation.framework/Foundation' -foundation = cdll.LoadLibrary(util.find_library('Foundation')) +foundation = cdll.LoadLibrary(lib) foundation.NSMouseInRect.restype = c_bool foundation.NSMouseInRect.argtypes = [NSPoint, NSRect, c_bool] diff -Nru pyglet-1.4.10/pyglet/libs/darwin/cocoapy/runtime.py pyglet-1.5.14/pyglet/libs/darwin/cocoapy/runtime.py --- pyglet-1.4.10/pyglet/libs/darwin/cocoapy/runtime.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/darwin/cocoapy/runtime.py 2020-12-31 21:11:00.000000000 +0000 @@ -40,6 +40,7 @@ __LP64__ = (8*struct.calcsize("P") == 64) __i386__ = (platform.machine() == 'i386') +__arm64__ = (platform.machine() == 'arm64') if sizeof(c_void_p) == 4: c_ptrdiff_t = c_int32 @@ -48,7 +49,13 @@ ###################################################################### -objc = cdll.LoadLibrary(util.find_library('objc')) +lib = util.find_library('objc') + +# Hack for compatibility with macOS > 11.0 +if lib is None: + lib = '/usr/lib/libobjc.dylib' + +objc = cdll.LoadLibrary(lib) ###################################################################### @@ -129,9 +136,11 @@ objc.class_getMethodImplementation.restype = c_void_p objc.class_getMethodImplementation.argtypes = [c_void_p, c_void_p] -# IMP class_getMethodImplementation_stret(Class cls, SEL name) -objc.class_getMethodImplementation_stret.restype = c_void_p -objc.class_getMethodImplementation_stret.argtypes = [c_void_p, c_void_p] +# The function is marked as OBJC_ARM64_UNAVAILABLE. +if not __arm64__: + # IMP class_getMethodImplementation_stret(Class cls, SEL name) + objc.class_getMethodImplementation_stret.restype = c_void_p + objc.class_getMethodImplementation_stret.argtypes = [c_void_p, c_void_p] # const char * class_getName(Class cls) objc.class_getName.restype = c_char_p @@ -278,14 +287,18 @@ # id objc_msgSend(id theReceiver, SEL theSelector, ...) # id objc_msgSendSuper(struct objc_super *super, SEL op, ...) -# void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) -objc.objc_msgSendSuper_stret.restype = None +# The function is marked as OBJC_ARM64_UNAVAILABLE. +if not __arm64__: + # void objc_msgSendSuper_stret(struct objc_super *super, SEL op, ...) + objc.objc_msgSendSuper_stret.restype = None # double objc_msgSend_fpret(id self, SEL op, ...) # objc.objc_msgSend_fpret.restype = c_double -# void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...) -objc.objc_msgSend_stret.restype = None +# The function is marked as OBJC_ARM64_UNAVAILABLE. +if not __arm64__: + # void objc_msgSend_stret(void * stretAddr, id theReceiver, SEL theSelector, ...) + objc.objc_msgSend_stret.restype = None # void objc_registerClassPair(Class cls) objc.objc_registerClassPair.restype = None @@ -481,12 +494,19 @@ OBJC_SUPER_PTR = POINTER(OBJC_SUPER) -#http://stackoverflow.com/questions/3095360/what-exactly-is-super-in-objective-c -def send_super(receiver, selName, *args, **kwargs): - #print 'send_super', receiver, selName, args +# http://stackoverflow.com/questions/3095360/what-exactly-is-super-in-objective-c +# +# `superclass_name` is optional and can be used to force finding the superclass +# by name. It is used to circumvent a bug in which the superclass was resolved +# incorrectly which lead to an infinite recursion: +# https://github.com/pyglet/pyglet/issues/5 +def send_super(receiver, selName, *args, superclass_name=None, **kwargs): if hasattr(receiver, '_as_parameter_'): receiver = receiver._as_parameter_ - superclass = get_superclass_of_object(receiver) + if superclass_name is None: + superclass = get_superclass_of_object(receiver) + else: + superclass = get_class(superclass_name) super_struct = OBJC_SUPER(receiver, superclass) selector = get_selector(selName) restype = kwargs.get('restype', c_void_p) @@ -651,7 +671,7 @@ ###################################################################### -class ObjCMethod(object): +class ObjCMethod: """This represents an unbound Objective-C method (really an IMP).""" # Note, need to map 'c' to c_byte rather than c_char, because otherwise @@ -768,7 +788,7 @@ ###################################################################### -class ObjCBoundMethod(object): +class ObjCBoundMethod: """This represents an Objective-C method (an IMP) which has been bound to some id which will be passed as the first parameter to the method.""" @@ -786,7 +806,7 @@ ###################################################################### -class ObjCClass(object): +class ObjCClass: """Python wrapper for an Objective-C class.""" # We only create one Python object for each Objective-C class. @@ -909,7 +929,7 @@ ###################################################################### -class ObjCInstance(object): +class ObjCInstance: """Python wrapper for an Objective-C instance.""" _cached_objects = {} @@ -1056,7 +1076,7 @@ # myclass = ObjCClass('MySubclassName') # myinstance = myclass.alloc().init() # -class ObjCSubclass(object): +class ObjCSubclass: """Use this to create a subclass of an existing Objective-C class. It consists primarily of function decorators which you use to add methods to the subclass.""" @@ -1166,7 +1186,7 @@ # to be careful to not create another ObjCInstance here (which # happens when the usual method decorator turns the self argument # into an ObjCInstance), or else get trapped in an infinite recursion. -class DeallocationObserver_Implementation(object): +class DeallocationObserver_Implementation: DeallocationObserver = ObjCSubclass('NSObject', 'DeallocationObserver', register=False) DeallocationObserver.add_ivar('observed_object', c_void_p) DeallocationObserver.register() diff -Nru pyglet-1.4.10/pyglet/libs/darwin/__init__.py pyglet-1.5.14/pyglet/libs/darwin/__init__.py --- pyglet-1.4.10/pyglet/libs/darwin/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/darwin/__init__.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,7 +32,5 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import from .cocoapy import * - diff -Nru pyglet-1.4.10/pyglet/libs/darwin/quartzkey.py pyglet-1.5.14/pyglet/libs/darwin/quartzkey.py --- pyglet-1.4.10/pyglet/libs/darwin/quartzkey.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/darwin/quartzkey.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,12 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' - -__docformat__ = 'restructuredtext' -__version__ = '' - from pyglet.window import key # From SDL: src/video/quartz/SDL_QuartzKeys.h @@ -230,77 +224,76 @@ QZ_KP_PERIOD: key.NUM_DECIMAL, } - charmap = { - ' ' : key.SPACE, - '!' : key.EXCLAMATION, - '"' : key.DOUBLEQUOTE, - '#' : key.HASH, - '#' : key.POUND, - '$' : key.DOLLAR, - '%' : key.PERCENT, - '&' : key.AMPERSAND, - "'" : key.APOSTROPHE, - '(' : key.PARENLEFT, - ')' : key.PARENRIGHT, - '*' : key.ASTERISK, - '+' : key.PLUS, - ',' : key.COMMA, - '-' : key.MINUS, - '.' : key.PERIOD, - '/' : key.SLASH, - '0' : key._0, - '1' : key._1, - '2' : key._2, - '3' : key._3, - '4' : key._4, - '5' : key._5, - '6' : key._6, - '7' : key._7, - '8' : key._8, - '9' : key._9, - ':' : key.COLON, - ';' : key.SEMICOLON, - '<' : key.LESS, - '=' : key.EQUAL, - '>' : key.GREATER, - '?' : key.QUESTION, - '@' : key.AT, - '[' : key.BRACKETLEFT, - '\\' : key.BACKSLASH, - ']' : key.BRACKETRIGHT, - '^' : key.ASCIICIRCUM, - '_' : key.UNDERSCORE, - '`' : key.GRAVE, - '`' : key.QUOTELEFT, - 'A' : key.A, - 'B' : key.B, - 'C' : key.C, - 'D' : key.D, - 'E' : key.E, - 'F' : key.F, - 'G' : key.G, - 'H' : key.H, - 'I' : key.I, - 'J' : key.J, - 'K' : key.K, - 'L' : key.L, - 'M' : key.M, - 'N' : key.N, - 'O' : key.O, - 'P' : key.P, - 'Q' : key.Q, - 'R' : key.R, - 'S' : key.S, - 'T' : key.T, - 'U' : key.U, - 'V' : key.V, - 'W' : key.W, - 'X' : key.X, - 'Y' : key.Y, - 'Z' : key.Z, - '{' : key.BRACELEFT, - '|' : key.BAR, - '}' : key.BRACERIGHT, - '~' : key.ASCIITILDE + ' ': key.SPACE, + '!': key.EXCLAMATION, + '"': key.DOUBLEQUOTE, + '#': key.HASH, + '#': key.POUND, + '$': key.DOLLAR, + '%': key.PERCENT, + '&': key.AMPERSAND, + "'": key.APOSTROPHE, + '(': key.PARENLEFT, + ')': key.PARENRIGHT, + '*': key.ASTERISK, + '+': key.PLUS, + ',': key.COMMA, + '-': key.MINUS, + '.': key.PERIOD, + '/': key.SLASH, + '0': key._0, + '1': key._1, + '2': key._2, + '3': key._3, + '4': key._4, + '5': key._5, + '6': key._6, + '7': key._7, + '8': key._8, + '9': key._9, + ':': key.COLON, + ';': key.SEMICOLON, + '<': key.LESS, + '=': key.EQUAL, + '>': key.GREATER, + '?': key.QUESTION, + '@': key.AT, + '[': key.BRACKETLEFT, + '\\': key.BACKSLASH, + ']': key.BRACKETRIGHT, + '^': key.ASCIICIRCUM, + '_': key.UNDERSCORE, + '`': key.GRAVE, + '`': key.QUOTELEFT, + 'A': key.A, + 'B': key.B, + 'C': key.C, + 'D': key.D, + 'E': key.E, + 'F': key.F, + 'G': key.G, + 'H': key.H, + 'I': key.I, + 'J': key.J, + 'K': key.K, + 'L': key.L, + 'M': key.M, + 'N': key.N, + 'O': key.O, + 'P': key.P, + 'Q': key.Q, + 'R': key.R, + 'S': key.S, + 'T': key.T, + 'U': key.U, + 'V': key.V, + 'W': key.W, + 'X': key.X, + 'Y': key.Y, + 'Z': key.Z, + '{': key.BRACELEFT, + '|': key.BAR, + '}': key.BRACERIGHT, + '~': key.ASCIITILDE } diff -Nru pyglet-1.4.10/pyglet/libs/win32/constants.py pyglet-1.5.14/pyglet/libs/win32/constants.py --- pyglet-1.4.10/pyglet/libs/win32/constants.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/win32/constants.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,6 +32,8 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- +import sys + # Most of this file is win32con.py from Python for Windows Extensions: # http://www.python.net/crew/mhammond/win32/ @@ -1187,8 +1189,17 @@ VK_MEDIA_NEXT_TRACK = 0xB0 VK_MEDIA_PREV_TRACK = 0xB1 VK_MEDIA_PLAY_PAUSE = 0xB3 +VK_LAUNCH_MAIL = 0xB4 +VK_LAUNCH_MEDIA_SELECT = 0xB5 +VK_LAUNCH_APP1 = 0xB6 +VK_LAUNCH_APP2 = 0xB VK_BROWSER_BACK = 0xA6 VK_BROWSER_FORWARD = 0xA7 +VK_BROWSER_REFRESH = 0xA8 +VK_BROWSER_STOP = 0xA9 +VK_BROWSER_SEARCH = 0xAA +VK_BROWSER_FAVORITES = 0xAB +VK_BROWSER_HOME = 0xAC WH_MIN = (-1) WH_MSGFILTER = (-1) WH_JOURNALRECORD = 0 @@ -1366,6 +1377,7 @@ WM_WINDOWPOSCHANGING = 70 WM_WINDOWPOSCHANGED = 71 WM_POWER = 72 +WM_COPYGLOBALDATA = 73 PWR_OK = 1 PWR_FAIL = (-1) PWR_SUSPENDREQUEST = 1 @@ -5002,6 +5014,15 @@ RIDEV_NOHOTKEYS = 0x00000200 RIDEV_APPKEYS = 0x00000400 RIDEV_EXMODEMASK = 0x000000F0 +RIDEV_EXINPUTSINK = 0x00001000 # Vista+ +RIDEV_DEVNOTIFY = 0x00002000 # Vista+ + +RI_KEY_MAKE = 0 +RI_KEY_BREAK = 1 +RI_KEY_E0 = 2 +RI_KEY_E1 = 4 +RI_KEY_TERMSRV_SET_LED = 8 +RI_KEY_TERMSRV_SHADOW = 0x10 RIM_TYPEMOUSE = 0 RIM_TYPEKEYBOARD = 1 @@ -5035,3 +5056,34 @@ RI_MOUSE_BUTTON_5_UP = 0x0200 RI_MOUSE_WHEEL = 0x0400 + +WINDOWS_VISTA_OR_GREATER = sys.getwindowsversion() >= (6, 0) +WINDOWS_7_OR_GREATER = sys.getwindowsversion() >= (6, 1) +WINDOWS_8_OR_GREATER = sys.getwindowsversion() >= (6, 2) +WINDOWS_8_1_OR_GREATER = sys.getwindowsversion() >= (6, 3) +WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER = sys.getwindowsversion() >= (10, 0, 14393) # 1607 +WINDOWS_10_CREATORS_UPDATE_OR_GREATER = sys.getwindowsversion() >= (10, 0, 15063) # 1703 + +MSGFLT_ALLOW = 1 +MSGFLT_DISALLOW = 2 +MSGFLT_RESET = 0 + +COINIT_APARTMENTTHREADED = 0x2 +COINIT_MULTITHREADED = 0x0 +COINIT_DISABLE_OLE1DDE = 0x4 +COINIT_SPEED_OVER_MEMORY = 0x8 + +MF_ACCESSMODE_READ = 1 +MF_ACCESSMODE_WRITE = 2 +MF_ACCESSMODE_READWRITE = 3 + +MF_OPENMODE_FAIL_IF_NOT_EXIST = 0 +MF_OPENMODE_FAIL_IF_EXIST = 1 +MF_OPENMODE_RESET_IF_EXIST = 2 +MF_OPENMODE_APPEND_IF_EXIST = 3 +MF_OPENMODE_DELETE_IF_EXIST = 4 + +MF_FILEFLAGS_NONE = 0 +MF_FILEFLAGS_NOBUFFERING = 1 + +CLSCTX_INPROC_SERVER = 0x1 diff -Nru pyglet-1.4.10/pyglet/libs/win32/dinput.py pyglet-1.5.14/pyglet/libs/win32/dinput.py --- pyglet-1.4.10/pyglet/libs/win32/dinput.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/win32/dinput.py 2020-12-31 21:03:32.000000000 +0000 @@ -337,7 +337,7 @@ # IDirect* interfaces are all Unicode (e.g. IDirectInputDevice8W). -class IDirectInputDevice8(com.IUnknown): +class IDirectInputDevice8(com.pIUnknown): _methods_ = [ ('GetCapabilities', com.STDMETHOD()), @@ -399,7 +399,7 @@ com.STDMETHOD()), ] -class IDirectInput8(com.IUnknown): +class IDirectInput8(com.pIUnknown): _methods_ = [ ('CreateDevice', com.STDMETHOD(ctypes.POINTER(com.GUID), diff -Nru pyglet-1.4.10/pyglet/libs/win32/__init__.py pyglet-1.5.14/pyglet/libs/win32/__init__.py --- pyglet-1.4.10/pyglet/libs/win32/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/win32/__init__.py 2020-11-16 14:49:52.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -33,16 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id: $ -from __future__ import print_function -from __future__ import absolute_import -from builtins import object import struct import pyglet from . import constants from .types import * +from pyglet import com IS64 = struct.calcsize("P") == 8 @@ -67,7 +63,7 @@ c_void_p()) return msg.value - class DebugLibrary(object): + class DebugLibrary: def __init__(self, lib): self.lib = lib @@ -93,6 +89,8 @@ _kernel32 = DebugLibrary(windll.kernel32) _user32 = DebugLibrary(windll.user32) _dwmapi = DebugLibrary(windll.dwmapi) +_shell32 = DebugLibrary(windll.shell32) +_ole32 = DebugLibrary(windll.ole32) # _gdi32 _gdi32.AddFontMemResourceEx.restype = HANDLE @@ -273,9 +271,32 @@ _user32.RegisterRawInputDevices.argtypes = [PCRAWINPUTDEVICE, UINT, UINT] _user32.GetRawInputData.restype = UINT _user32.GetRawInputData.argtypes = [HRAWINPUT, UINT, LPVOID, PUINT, UINT] +_user32.ChangeWindowMessageFilterEx.restype = BOOL +_user32.ChangeWindowMessageFilterEx.argtypes = [HWND, UINT, DWORD, c_void_p] #dwmapi _dwmapi.DwmIsCompositionEnabled.restype = c_int _dwmapi.DwmIsCompositionEnabled.argtypes = [POINTER(INT)] _dwmapi.DwmFlush.restype = c_int _dwmapi.DwmFlush.argtypes = [] + +#_shell32 +_shell32.DragAcceptFiles.restype = c_void +_shell32.DragAcceptFiles.argtypes = [HWND, BOOL] +_shell32.DragFinish.restype = c_void +_shell32.DragFinish.argtypes = [HDROP] +_shell32.DragQueryFileW.restype = UINT +_shell32.DragQueryFileW.argtypes = [HDROP, UINT, LPWSTR, UINT] +_shell32.DragQueryPoint.restype = BOOL +_shell32.DragQueryPoint.argtypes = [HDROP, LPPOINT] + +# ole32 +_ole32.CreateStreamOnHGlobal.argtypes = [HGLOBAL, BOOL, LPSTREAM] +_ole32.CoInitializeEx.restype = HRESULT +_ole32.CoInitializeEx.argtypes = [LPVOID, DWORD] +_ole32.CoUninitialize.restype = HRESULT +_ole32.CoUninitialize.argtypes = [] +_ole32.PropVariantClear.restype = HRESULT +_ole32.PropVariantClear.argtypes = [c_void_p] +_ole32.CoCreateInstance.restype = HRESULT +_ole32.CoCreateInstance.argtypes = [com.REFIID, c_void_p, DWORD, com.REFIID, c_void_p] diff -Nru pyglet-1.4.10/pyglet/libs/win32/libwintab.py pyglet-1.5.14/pyglet/libs/win32/libwintab.py --- pyglet-1.4.10/pyglet/libs/win32/libwintab.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/win32/libwintab.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -#!/usr/bin/python # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -33,9 +32,7 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ -from __future__ import division import ctypes lib = ctypes.windll.wintab32 @@ -51,6 +48,7 @@ LCNAMELEN = 40 + class AXIS(ctypes.Structure): _fields_ = ( ('axMin', LONG), @@ -65,6 +63,7 @@ def get_bias(self): return -self.axMin + class ORIENTATION(ctypes.Structure): _fields_ = ( ('orAzimuth', ctypes.c_int), @@ -72,6 +71,7 @@ ('orTwist', ctypes.c_int) ) + class ROTATION(ctypes.Structure): _fields_ = ( ('roPitch', ctypes.c_int), @@ -79,6 +79,7 @@ ('roYaw', ctypes.c_int), ) + class LOGCONTEXT(ctypes.Structure): _fields_ = ( ('lcName', WCHAR * LCNAMELEN), @@ -117,7 +118,8 @@ ('lcSysSensY', FIX32), ) -# Custom packet format with fields + +# Custom packet format with fields # PK_CHANGED # PK_CURSOR # PK_BUTTONS @@ -140,20 +142,21 @@ ('pkOrientation', ORIENTATION), ) -PK_CONTEXT = 0x0001 # reporting context -PK_STATUS = 0x0002 # status bits -PK_TIME = 0x0004 # time stamp -PK_CHANGED = 0x0008 # change bit vector -PK_SERIAL_NUMBER = 0x0010 # packet serial number -PK_CURSOR = 0x0020 # reporting cursor -PK_BUTTONS = 0x0040 # button information -PK_X = 0x0080 # x axis -PK_Y = 0x0100 # y axis -PK_Z = 0x0200 # z axis -PK_NORMAL_PRESSURE = 0x0400 # normal or tip pressure -PK_TANGENT_PRESSURE = 0x0800 # tangential or barrel pressure -PK_ORIENTATION = 0x1000 # orientation info: tilts -PK_ROTATION = 0x2000 # rotation info; 1.1 + +PK_CONTEXT = 0x0001 # reporting context +PK_STATUS = 0x0002 # status bits +PK_TIME = 0x0004 # time stamp +PK_CHANGED = 0x0008 # change bit vector +PK_SERIAL_NUMBER = 0x0010 # packet serial number +PK_CURSOR = 0x0020 # reporting cursor +PK_BUTTONS = 0x0040 # button information +PK_X = 0x0080 # x axis +PK_Y = 0x0100 # y axis +PK_Z = 0x0200 # z axis +PK_NORMAL_PRESSURE = 0x0400 # normal or tip pressure +PK_TANGENT_PRESSURE = 0x0800 # tangential or barrel pressure +PK_ORIENTATION = 0x1000 # orientation info: tilts +PK_ROTATION = 0x2000 # rotation info; 1.1 TU_NONE = 0 TU_INCHES = 1 @@ -163,7 +166,7 @@ # messages WT_DEFBASE = 0x7ff0 WT_MAXOFFSET = 0xf -WT_PACKET = 0 # remember to add base +WT_PACKET = 0 # remember to add base WT_CTXOPEN = 1 WT_CTXCLOSE = 2 WT_CTXUPDATE = 3 @@ -204,11 +207,11 @@ HWC_INTEGRATED = 0x0001 HWC_TOUCH = 0x0002 HWC_HARDPROX = 0x0004 -HWC_PHYSID_CURSORS = 0x0008 # 1.1 +HWC_PHYSID_CURSORS = 0x0008 # 1.1 -CRC_MULTIMODE = 0x0001 # 1.1 -CRC_AGGREGATE = 0x0002 # 1.1 -CRC_INVERT = 0x0004 # 1.1 +CRC_MULTIMODE = 0x0001 # 1.1 +CRC_AGGREGATE = 0x0002 # 1.1 +CRC_INVERT = 0x0004 # 1.1 WTI_INTERFACE = 1 IFC_WINTABID = 1 @@ -236,8 +239,8 @@ WTI_DEFCONTEXT = 3 WTI_DEFSYSCTX = 4 -WTI_DDCTXS = 400 # 1.1 -WTI_DSCTXS = 500 # 1.1 +WTI_DDCTXS = 400 # 1.1 +WTI_DSCTXS = 500 # 1.1 CTX_NAME = 1 CTX_OPTIONS = 2 CTX_STATUS = 3 @@ -292,8 +295,8 @@ DVC_NPRESSURE = 15 DVC_TPRESSURE = 16 DVC_ORIENTATION = 17 -DVC_ROTATION = 18 # 1.1 -DVC_PNPID = 19 # 1.1 +DVC_ROTATION = 18 # 1.1 +DVC_PNPID = 19 # 1.1 DVC_MAX = 19 WTI_CURSORS = 200 @@ -311,12 +314,12 @@ CSR_TPBUTTON = 12 CSR_TPBTNMARKS = 13 CSR_TPRESPONSE = 14 -CSR_PHYSID = 15 # 1.1 -CSR_MODE = 16 # 1.1 -CSR_MINPKTDATA = 17 # 1.1 -CSR_MINBUTTONS = 18 # 1.1 -CSR_CAPABILITIES = 19 # 1.1 -CSR_TYPE = 20 # 1.2 +CSR_PHYSID = 15 # 1.1 +CSR_MODE = 16 # 1.1 +CSR_MINPKTDATA = 17 # 1.1 +CSR_MINBUTTONS = 18 # 1.1 +CSR_CAPABILITIES = 19 # 1.1 +CSR_TYPE = 20 # 1.2 CSR_MAX = 20 WTI_EXTENSIONS = 300 @@ -328,14 +331,14 @@ EXT_DEFAULT = 6 EXT_DEFCONTEXT = 7 EXT_DEFSYSCTX = 8 -EXT_CURSORS = 9 -EXT_MAX = 109 # Allow 100 cursors +EXT_CURSORS = 9 +EXT_MAX = 109 # Allow 100 cursors CXO_SYSTEM = 0x0001 CXO_PEN = 0x0002 CXO_MESSAGES = 0x0004 CXO_MARGIN = 0x8000 CXO_MGNINSIDE = 0x4000 -CXO_CSRMESSAGES = 0x0008 # 1.1 +CXO_CSRMESSAGES = 0x0008 # 1.1 # context status values CXS_DISABLED = 0x0001 @@ -353,7 +356,7 @@ TPS_QUEUE_ERR = 0x0002 TPS_MARGIN = 0x0004 TPS_GRAB = 0x0008 -TPS_INVERT = 0x0010 # 1.1 +TPS_INVERT = 0x0010 # 1.1 TBN_NONE = 0 TBN_UP = 1 @@ -362,9 +365,9 @@ PKEXT_RELATIVE = 2 # Extension tags. -WTX_OBT = 0 # Out of bounds tracking -WTX_FKEYS = 1 # Function keys -WTX_TILT = 2 # Raw Cartesian tilt; 1.1 -WTX_CSRMASK = 3 # select input by cursor type; 1.1 -WTX_XBTNMASK = 4 # Extended button mask; 1.1 -WTX_EXPKEYS = 5 # ExpressKeys; 1.3 +WTX_OBT = 0 # Out of bounds tracking +WTX_FKEYS = 1 # Function keys +WTX_TILT = 2 # Raw Cartesian tilt; 1.1 +WTX_CSRMASK = 3 # select input by cursor type; 1.1 +WTX_XBTNMASK = 4 # Extended button mask; 1.1 +WTX_EXPKEYS = 5 # ExpressKeys; 1.3 diff -Nru pyglet-1.4.10/pyglet/libs/win32/types.py pyglet-1.5.14/pyglet/libs/win32/types.py --- pyglet-1.4.10/pyglet/libs/win32/types.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/win32/types.py 2020-12-31 21:03:32.000000000 +0000 @@ -33,12 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -""" -""" - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - import sys import ctypes from ctypes import * @@ -102,6 +96,7 @@ LONG_PTR = HANDLE HDROP = HANDLE LPTSTR = LPWSTR +LPSTREAM = c_void_p LF_FACESIZE = 32 CCHDEVICENAME = 32 @@ -471,3 +466,25 @@ ('header', RAWINPUTHEADER), ('data', _RAWINPUTDEVICEUNION), ] + + +# PROPVARIANT wrapper, doesn't require InitPropVariantFromInt64 this way. +class _VarTable(ctypes.Union): + """Must be in an anonymous union or values will not work across various VT's.""" + _fields_ = [ + ('llVal', ctypes.c_longlong), + ('pwszVal', LPWSTR) + ] + + +class PROPVARIANT(ctypes.Structure): + _anonymous_ = ['union'] + + _fields_ = [ + ('vt', ctypes.c_ushort), + ('wReserved1', ctypes.c_ubyte), + ('wReserved2', ctypes.c_ubyte), + ('wReserved3', ctypes.c_ulong), + ('union', _VarTable) + ] + diff -Nru pyglet-1.4.10/pyglet/libs/win32/winkey.py pyglet-1.5.14/pyglet/libs/win32/winkey.py --- pyglet-1.4.10/pyglet/libs/win32/winkey.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/win32/winkey.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,13 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from __future__ import absolute_import - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - from pyglet.window import key from .constants import * @@ -86,118 +79,118 @@ 0x14: key.CAPSLOCK, 0x5d: key.MENU, -# VK_LBUTTON: , -# VK_RBUTTON: , - VK_CANCEL: key.CANCEL, -# VK_MBUTTON: , -# VK_BACK: , - VK_TAB: key.TAB, -# VK_CLEAR: , - VK_RETURN: key.RETURN, - VK_SHIFT: key.LSHIFT, - VK_CONTROL: key.LCTRL, - VK_MENU: key.LALT, - VK_PAUSE: key.PAUSE, -# VK_CAPITAL: , -# VK_KANA: , -# VK_HANGEUL: , -# VK_HANGUL: , -# VK_JUNJA: , -# VK_FINAL: , -# VK_HANJA: , -# VK_KANJI: , - VK_ESCAPE: key.ESCAPE, -# VK_CONVERT: , -# VK_NONCONVERT: , -# VK_ACCEPT: , -# VK_MODECHANGE: , - VK_SPACE: key.SPACE, - VK_PRIOR: key.PAGEUP, - VK_NEXT: key.PAGEDOWN, - VK_END: key.END, - VK_HOME: key.HOME, - VK_LEFT: key.LEFT, - VK_UP: key.UP, - VK_RIGHT: key.RIGHT, - VK_DOWN: key.DOWN, -# VK_SELECT: , - VK_PRINT: key.PRINT, -# VK_EXECUTE: , -# VK_SNAPSHOT: , - VK_INSERT: key.INSERT, - VK_DELETE: key.DELETE, - VK_HELP: key.HELP, - VK_LWIN: key.LWINDOWS, - VK_RWIN: key.RWINDOWS, -# VK_APPS: , - VK_NUMPAD0: key.NUM_0, - VK_NUMPAD1: key.NUM_1, - VK_NUMPAD2: key.NUM_2, - VK_NUMPAD3: key.NUM_3, - VK_NUMPAD4: key.NUM_4, - VK_NUMPAD5: key.NUM_5, - VK_NUMPAD6: key.NUM_6, - VK_NUMPAD7: key.NUM_7, - VK_NUMPAD8: key.NUM_8, - VK_NUMPAD9: key.NUM_9, - VK_MULTIPLY: key.NUM_MULTIPLY, - VK_ADD: key.NUM_ADD, -# VK_SEPARATOR: , - VK_SUBTRACT: key.NUM_SUBTRACT, - VK_DECIMAL: key.NUM_DECIMAL, - VK_DIVIDE: key.NUM_DIVIDE, - VK_F1: key.F1, - VK_F2: key.F2, - VK_F3: key.F3, - VK_F4: key.F4, - VK_F5: key.F5, - VK_F6: key.F6, - VK_F7: key.F7, - VK_F8: key.F8, - VK_F9: key.F9, - VK_F10: key.F10, - VK_F11: key.F11, - VK_F12: key.F12, - VK_F13: key.F13, - VK_F14: key.F14, - VK_F15: key.F15, - VK_F16: key.F16, -# VK_F17: , -# VK_F18: , -# VK_F19: , -# VK_F20: , -# VK_F21: , -# VK_F22: , -# VK_F23: , -# VK_F24: , - VK_NUMLOCK: key.NUMLOCK, - VK_SCROLL: key.SCROLLLOCK, - VK_LSHIFT: key.LSHIFT, - VK_RSHIFT: key.RSHIFT, - VK_LCONTROL: key.LCTRL, - VK_RCONTROL: key.RCTRL, - VK_LMENU: key.LALT, - VK_RMENU: key.RALT, -# VK_PROCESSKEY: , -# VK_ATTN: , -# VK_CRSEL: , -# VK_EXSEL: , -# VK_EREOF: , -# VK_PLAY: , -# VK_ZOOM: , -# VK_NONAME: , -# VK_PA1: , -# VK_OEM_CLEAR: , -# VK_XBUTTON1: , -# VK_XBUTTON2: , -# VK_VOLUME_MUTE: , -# VK_VOLUME_DOWN: , -# VK_VOLUME_UP: , -# VK_MEDIA_NEXT_TRACK: , -# VK_MEDIA_PREV_TRACK: , -# VK_MEDIA_PLAY_PAUSE: , -# VK_BROWSER_BACK: , -# VK_BROWSER_FORWARD: , + # VK_LBUTTON: , + # VK_RBUTTON: , + VK_CANCEL: key.CANCEL, + # VK_MBUTTON: , + # VK_BACK: , + VK_TAB: key.TAB, + # VK_CLEAR: , + VK_RETURN: key.RETURN, + VK_SHIFT: key.LSHIFT, + VK_CONTROL: key.LCTRL, + VK_MENU: key.LALT, + VK_PAUSE: key.PAUSE, + # VK_CAPITAL: , + # VK_KANA: , + # VK_HANGEUL: , + # VK_HANGUL: , + # VK_JUNJA: , + # VK_FINAL: , + # VK_HANJA: , + # VK_KANJI: , + VK_ESCAPE: key.ESCAPE, + # VK_CONVERT: , + # VK_NONCONVERT: , + # VK_ACCEPT: , + # VK_MODECHANGE: , + VK_SPACE: key.SPACE, + VK_PRIOR: key.PAGEUP, + VK_NEXT: key.PAGEDOWN, + VK_END: key.END, + VK_HOME: key.HOME, + VK_LEFT: key.LEFT, + VK_UP: key.UP, + VK_RIGHT: key.RIGHT, + VK_DOWN: key.DOWN, + # VK_SELECT: , + VK_PRINT: key.PRINT, + # VK_EXECUTE: , + # VK_SNAPSHOT: , + VK_INSERT: key.INSERT, + VK_DELETE: key.DELETE, + VK_HELP: key.HELP, + VK_LWIN: key.LWINDOWS, + VK_RWIN: key.RWINDOWS, + # VK_APPS: , + VK_NUMPAD0: key.NUM_0, + VK_NUMPAD1: key.NUM_1, + VK_NUMPAD2: key.NUM_2, + VK_NUMPAD3: key.NUM_3, + VK_NUMPAD4: key.NUM_4, + VK_NUMPAD5: key.NUM_5, + VK_NUMPAD6: key.NUM_6, + VK_NUMPAD7: key.NUM_7, + VK_NUMPAD8: key.NUM_8, + VK_NUMPAD9: key.NUM_9, + VK_MULTIPLY: key.NUM_MULTIPLY, + VK_ADD: key.NUM_ADD, + # VK_SEPARATOR: , + VK_SUBTRACT: key.NUM_SUBTRACT, + VK_DECIMAL: key.NUM_DECIMAL, + VK_DIVIDE: key.NUM_DIVIDE, + VK_F1: key.F1, + VK_F2: key.F2, + VK_F3: key.F3, + VK_F4: key.F4, + VK_F5: key.F5, + VK_F6: key.F6, + VK_F7: key.F7, + VK_F8: key.F8, + VK_F9: key.F9, + VK_F10: key.F10, + VK_F11: key.F11, + VK_F12: key.F12, + VK_F13: key.F13, + VK_F14: key.F14, + VK_F15: key.F15, + VK_F16: key.F16, + # VK_F17: , + # VK_F18: , + # VK_F19: , + # VK_F20: , + # VK_F21: , + # VK_F22: , + # VK_F23: , + # VK_F24: , + VK_NUMLOCK: key.NUMLOCK, + VK_SCROLL: key.SCROLLLOCK, + VK_LSHIFT: key.LSHIFT, + VK_RSHIFT: key.RSHIFT, + VK_LCONTROL: key.LCTRL, + VK_RCONTROL: key.RCTRL, + VK_LMENU: key.LALT, + VK_RMENU: key.RALT, + # VK_PROCESSKEY: , + # VK_ATTN: , + # VK_CRSEL: , + # VK_EXSEL: , + # VK_EREOF: , + # VK_PLAY: , + # VK_ZOOM: , + # VK_NONAME: , + # VK_PA1: , + # VK_OEM_CLEAR: , + # VK_XBUTTON1: , + # VK_XBUTTON2: , + # VK_VOLUME_MUTE: , + # VK_VOLUME_DOWN: , + # VK_VOLUME_UP: , + # VK_MEDIA_NEXT_TRACK: , + # VK_MEDIA_PREV_TRACK: , + # VK_MEDIA_PLAY_PAUSE: , + # VK_BROWSER_BACK: , + # VK_BROWSER_FORWARD: , } # Keys that must be translated via MapVirtualKey, as the virtual key code diff -Nru pyglet-1.4.10/pyglet/libs/x11/cursorfont.py pyglet-1.5.14/pyglet/libs/x11/cursorfont.py --- pyglet-1.4.10/pyglet/libs/x11/cursorfont.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/x11/cursorfont.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,12 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - # /usr/include/X11/cursorfont.h XC_num_glyphs = 154 diff -Nru pyglet-1.4.10/pyglet/libs/x11/xf86vmode.py pyglet-1.5.14/pyglet/libs/x11/xf86vmode.py --- pyglet-1.4.10/pyglet/libs/x11/xf86vmode.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/x11/xf86vmode.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,16 +32,14 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for Xxf86vm +"""Wrapper for Xxf86vm Generated with: tools/genwrappers.py xf86vmode Do not modify this file. -''' +""" -__docformat__ = 'restructuredtext' -__version__ = '$Id$' import ctypes from ctypes import * diff -Nru pyglet-1.4.10/pyglet/libs/x11/xinerama.py pyglet-1.5.14/pyglet/libs/x11/xinerama.py --- pyglet-1.4.10/pyglet/libs/x11/xinerama.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/x11/xinerama.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,16 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for Xinerama +"""Wrapper for Xinerama Generated with: tools/genwrappers.py xinerama Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" import ctypes from ctypes import * diff -Nru pyglet-1.4.10/pyglet/libs/x11/xinput.py pyglet-1.5.14/pyglet/libs/x11/xinput.py --- pyglet-1.4.10/pyglet/libs/x11/xinput.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/x11/xinput.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,16 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for Xi +"""Wrapper for Xi Generated with: tools/genwrappers.py xinput Do not modify this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: wrap.py 1694 2008-01-30 23:12:00Z Alex.Holkner $' +""" import ctypes from ctypes import * @@ -73,6 +70,10 @@ sz_xGetExtensionVersionReply = 32 # /usr/include/X11/extensions/XI.h:57 sz_xListInputDevicesReq = 4 # /usr/include/X11/extensions/XI.h:58 sz_xListInputDevicesReply = 32 # /usr/include/X11/extensions/XI.h:59 +sz_xListDevicePropertiesReq = 8 # /usr/include/X11/extensions/XI.h +sz_xListDevicePropertiesReply = 32 # /usr/include/X11/extensions/XI.h +sz_xGetDevicePropertyReq = 24 # /usr/include/X11/extensions/XI.h +sz_xGetDevicePropertyReply = 32 # /usr/include/X11/extensions/XI.h sz_xOpenDeviceReq = 8 # /usr/include/X11/extensions/XI.h:60 sz_xOpenDeviceReply = 32 # /usr/include/X11/extensions/XI.h:61 sz_xCloseDeviceReq = 8 # /usr/include/X11/extensions/XI.h:62 @@ -1512,6 +1513,16 @@ XListInputDevices.restype = POINTER(XDeviceInfo) XListInputDevices.argtypes = [POINTER(Display), POINTER(c_int)] +# /usr/include/X11/extensions/XInput.h +XListDeviceProperties = _lib.XListDeviceProperties +XListDeviceProperties.restype = POINTER(Atom) +XListDeviceProperties.argtypes = [POINTER(Display), POINTER(XDevice), POINTER(c_int)] + +# /usr/include/X11/extensions/XInput.h +XGetDeviceProperty = _lib.XGetDeviceProperty +XGetDeviceProperty.restype = c_int +XGetDeviceProperty.argtypes = [POINTER(Display), POINTER(XDevice), Atom, c_long, c_long, c_bool, Atom, POINTER(Atom), POINTER(c_int), POINTER(c_ulong), POINTER(c_ulong), POINTER(c_char_p)] + # /usr/include/X11/extensions/XInput.h:5943 XFreeDeviceList = _lib.XFreeDeviceList XFreeDeviceList.restype = None @@ -1675,8 +1686,9 @@ 'XGetDeviceKeyMapping', 'XChangeDeviceKeyMapping', 'XGetDeviceModifierMapping', 'XSetDeviceModifierMapping', 'XSetDeviceButtonMapping', 'XGetDeviceButtonMapping', 'XQueryDeviceState', -'XFreeDeviceState', 'XGetExtensionVersion', 'XListInputDevices', -'XFreeDeviceList', 'XOpenDevice', 'XCloseDevice', 'XSetDeviceMode', +'XFreeDeviceState', 'XGetExtensionVersion', 'XListInputDevices', +'XListDeviceProperties', 'XGetDeviceProperty', 'XFreeDeviceList', +'XOpenDevice', 'XCloseDevice', 'XSetDeviceMode', 'XSetDeviceValuators', 'XGetDeviceControl', 'XChangeDeviceControl', 'XSelectExtensionEvent', 'XGetSelectedExtensionEvents', 'XChangeDeviceDontPropagateList', 'XGetDeviceDontPropagateList', diff -Nru pyglet-1.4.10/pyglet/libs/x11/xlib.py pyglet-1.5.14/pyglet/libs/x11/xlib.py --- pyglet-1.4.10/pyglet/libs/x11/xlib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/x11/xlib.py 2020-12-15 22:19:10.000000000 +0000 @@ -32,7 +32,7 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for X11 +"""Wrapper for X11 Generated with: tools/genwrappers.py xlib @@ -43,10 +43,7 @@ This file has been manually modified to fix a limitation of the current wrapping tools. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" import ctypes from ctypes import * diff -Nru pyglet-1.4.10/pyglet/libs/x11/xsync.py pyglet-1.5.14/pyglet/libs/x11/xsync.py --- pyglet-1.4.10/pyglet/libs/x11/xsync.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/libs/x11/xsync.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,17 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for Xext +"""Wrapper for Xext Generated with: tools/genwrappers.py xsync Do not modify this file. -''' -from __future__ import absolute_import - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" import ctypes from ctypes import * diff -Nru pyglet-1.4.10/pyglet/math.py pyglet-1.5.14/pyglet/math.py --- pyglet-1.4.10/pyglet/math.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/math.py 2020-12-23 15:47:40.000000000 +0000 @@ -0,0 +1,279 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Matrix and Vector operations. + +This module provides classes for Matrix and Vector math. +A :py:class:`~pyglet.matrix.Mat4` class is available for representing +4x4 matricies, including helper methods for rotating, scaling, and +transforming. The internal datatype of :py:class:`~pyglet.matrix.Mat4` +is a 1-dimensional array, so instances can be passed directly to OpenGL. + +""" + +import math as _math +import operator as _operator +import warnings as _warnings + + +def create_orthogonal(left, right, bottom, top, znear, zfar): + """Create a Mat4 orthographic projection matrix.""" + width = right - left + height = top - bottom + depth = zfar - znear + + sx = 2.0 / width + sy = 2.0 / height + sz = 2.0 / -depth + + tx = -(right + left) / width + ty = -(top + bottom) / height + tz = -(zfar + znear) / depth + + return Mat4((sx, 0.0, 0.0, 0.0, + 0.0, sy, 0.0, 0.0, + 0.0, 0.0, sz, 0.0, + tx, ty, tz, 1.0)) + + +def create_perspective(left, right, bottom, top, znear, zfar, fov=60): + """Create a Mat4 perspective projection matrix.""" + width = right - left + height = top - bottom + aspect = width / height + + xymax = znear * _math.tan(fov * _math.pi / 360) + ymin = -xymax + xmin = -xymax + + width = xymax - xmin + height = xymax - ymin + depth = zfar - znear + q = -(zfar + znear) / depth + qn = -2 * zfar * znear / depth + + w = 2 * znear / width + w = w / aspect + h = 2 * znear / height + + return Mat4((w, 0, 0, 0, + 0, h, 0, 0, + 0, 0, q, -1, + 0, 0, qn, 0)) + + +class Mat4(tuple): + """A 4x4 Matrix + + `Mat4` is a simple immutable 4x4 Matrix, with a few operators. + Two types of multiplication are possible. The "*" operator + will perform elementwise multiplication, wheras the "@" + operator will perform Matrix multiplication. Internally, + data is stored in a linear 1D array, allowing direct passing + to OpenGL. + """ + + def __new__(cls, values=None): + """Create a 4x4 Matrix + + A Matrix can be created with a list or tuple of 16 values. + If no values are provided, an "identity matrix" will be created + (1.0 on the main diagonal). Matrix objects are immutable, so + all operations return a new Mat4 object. + + :Parameters: + `values` : tuple of float or int + A tuple or list containing 16 floats or ints. + """ + assert values is None or len(values) == 16, "A 4x4 Matrix requires 16 values" + return super().__new__(Mat4, values or (1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0)) + + def row(self, index): + """Get a specific row as a tuple.""" + return self[index*4:index*4+4] + + def column(self, index): + """Get a specific column as a tuple.""" + return self[index::4] + + def scale(self, x=1, y=1, z=1): + """Get a scale Matrix on x, y, or z axis.""" + temp = list(self) + temp[0] *= x + temp[5] *= y + temp[10] *= z + return Mat4(temp) + + def translate(self, x=0, y=0, z=0): + """Get a translate Matrix along x, y, and z axis.""" + return Mat4(self) @ Mat4((1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, x, y, z, 1)) + + def rotate(self, angle=0, x=0, y=0, z=0): + """Get a rotation Matrix on x, y, or z axis.""" + assert all(abs(n) <= 1 for n in (x, y, z)), "x,y,z must be normalized (<=1)" + c = _math.cos(angle) + s = _math.sin(angle) + t = 1 - c + tempx, tempy, tempz = t * x, t * y, t * z + + ra = c + tempx * x + rb = 0 + tempx * y + s * z + rc = 0 + tempx * z - s * y + re = 0 + tempy * x - s * z + rf = c + tempy * y + rg = 0 + tempy * z + s * x + ri = 0 + tempz * x + s * y + rj = 0 + tempz * y - s * x + rk = c + tempz * z + + # ra, rb, rc, -- + # re, rf, rg, -- + # ri, rj, rk, -- + # --, --, --, -- + + return Mat4(self) @ Mat4((ra, rb, rc, 0, re, rf, rg, 0, ri, rj, rk, 0, 0, 0, 0, 1)) + + def transpose(self): + """Get a tranpose of this Matrix.""" + return Mat4(self[0::4] + self[1::4] + self[2::4] + self[3::4]) + + def __add__(self, other): + assert len(other) == 16, "Can only add to other Mat4 types" + return Mat4(tuple(s + o for s, o in zip(self, other))) + + def __sub__(self, other): + assert len(other) == 16, "Can only subtract from other Mat4 types" + return Mat4(tuple(s - o for s, o in zip(self, other))) + + def __pos__(self): + return self + + def __neg__(self): + return Mat4(tuple(-v for v in self)) + + def __invert__(self): + a = self[10] * self[15] - self[11] * self[14] + b = self[9] * self[15] - self[11] * self[13] + c = self[9] * self[14] - self[10] * self[13] + d = self[8] * self[15] - self[11] * self[12] + e = self[8] * self[14] - self[10] * self[12] + f = self[8] * self[13] - self[9] * self[12] + g = self[6] * self[15] - self[7] * self[14] + h = self[5] * self[15] - self[7] * self[13] + i = self[5] * self[14] - self[6] * self[13] + j = self[6] * self[11] - self[7] * self[10] + k = self[5] * self[11] - self[7] * self[9] + l = self[5] * self[10] - self[6] * self[9] + m = self[4] * self[15] - self[7] * self[12] + n = self[4] * self[14] - self[6] * self[12] + o = self[4] * self[11] - self[7] * self[8] + p = self[4] * self[10] - self[6] * self[8] + q = self[4] * self[13] - self[5] * self[12] + r = self[4] * self[9] - self[5] * self[8] + + det = (self[0] * (self[5] * a - self[6] * b + self[7] * c) + - self[1] * (self[4] * a - self[6] * d + self[7] * e) + + self[2] * (self[4] * b - self[5] * d + self[7] * f) + - self[3] * (self[4] * c - self[5] * e + self[6] * f)) + + if det == 0: + _warnings.warn("Unable to calculate inverse of singular Matrix") + return self + + pdet = 1 / det + ndet = -pdet + + return Mat4((pdet * (self[5] * a - self[6] * b + self[7] * c), + ndet * (self[1] * a - self[2] * b + self[3] * c), + pdet * (self[1] * g - self[2] * h + self[3] * i), + ndet * (self[1] * j - self[2] * k + self[3] * l), + ndet * (self[4] * a - self[6] * d + self[7] * e), + pdet * (self[0] * a - self[2] * d + self[3] * e), + ndet * (self[0] * g - self[2] * m + self[3] * n), + pdet * (self[0] * j - self[2] * o + self[3] * p), + pdet * (self[4] * b - self[5] * d + self[7] * f), + ndet * (self[0] * b - self[1] * d + self[3] * f), + pdet * (self[0] * h - self[1] * m + self[3] * q), + ndet * (self[0] * k - self[1] * o + self[3] * r), + ndet * (self[4] * c - self[5] * e + self[6] * f), + pdet * (self[0] * c - self[1] * e + self[2] * f), + ndet * (self[0] * i - self[1] * n + self[2] * q), + pdet * (self[0] * l - self[1] * p + self[2] * r))) + + def __round__(self, n=None): + return Mat4(tuple(round(v, n) for v in self)) + + def __mul__(self, other): + raise NotImplementedError("Please use the @ operator for Matrix multiplication.") + + def __matmul__(self, other): + assert len(other) == 16, "Can only multiply with other Mat4 types" + # Rows: + r0 = self[0:4] + r1 = self[4:8] + r2 = self[8:12] + r3 = self[12:16] + # Columns: + c0 = other[0::4] + c1 = other[1::4] + c2 = other[2::4] + c3 = other[3::4] + + # Multiply and sum rows * colums: + return Mat4((sum(map(_operator.mul, r0, c0)), + sum(map(_operator.mul, r0, c1)), + sum(map(_operator.mul, r0, c2)), + sum(map(_operator.mul, r0, c3)), + + sum(map(_operator.mul, r1, c0)), + sum(map(_operator.mul, r1, c1)), + sum(map(_operator.mul, r1, c2)), + sum(map(_operator.mul, r1, c3)), + + sum(map(_operator.mul, r2, c0)), + sum(map(_operator.mul, r2, c1)), + sum(map(_operator.mul, r2, c2)), + sum(map(_operator.mul, r2, c3)), + + sum(map(_operator.mul, r3, c0)), + sum(map(_operator.mul, r3, c1)), + sum(map(_operator.mul, r3, c2)), + sum(map(_operator.mul, r3, c3)))) + + def __repr__(self): + return f"{self.__class__.__name__}{self[0:4]}\n {self[4:8]}\n {self[8:12]}\n {self[12:16]}" diff -Nru pyglet-1.4.10/pyglet/media/buffered_logger.py pyglet-1.5.14/pyglet/media/buffered_logger.py --- pyglet-1.4.10/pyglet/media/buffered_logger.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/buffered_logger.py 2020-11-10 10:36:26.000000000 +0000 @@ -38,13 +38,12 @@ Handles accumulation of debug events while playing media_player and saves when sample's play ends. """ -from __future__ import division, print_function, unicode_literals -import pickle import time +import pickle -class BufferedLogger(object): +class BufferedLogger: def __init__(self, outfile): self.outfile = outfile self.log_entries = [] diff -Nru pyglet-1.4.10/pyglet/media/codecs/base.py pyglet-1.5.14/pyglet/media/codecs/base.py --- pyglet-1.4.10/pyglet/media/codecs/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/base.py 2020-12-31 21:03:32.000000000 +0000 @@ -1,6 +1,3 @@ -from __future__ import print_function -from __future__ import division -from builtins import object # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -36,13 +33,14 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -import ctypes +import io -from pyglet.compat import bytes_type, BytesIO -from pyglet.media.exceptions import MediaException, CannotSeekException +import pyglet +from pyglet.media.exceptions import MediaException, CannotSeekException, MediaEncodeException -class AudioFormat(object): + +class AudioFormat: """Audio details. An instance of this class is provided by sources with audio tracks. You @@ -81,7 +79,7 @@ self.sample_rate) -class VideoFormat(object): +class VideoFormat: """Video details. An instance of this class is provided by sources with a video stream. You @@ -117,7 +115,7 @@ return False -class AudioData(object): +class AudioData: """A single packet of audio data. This class is used internally by pyglet. @@ -169,39 +167,24 @@ elif num_bytes == 0: return - if not isinstance(self.data, str): - # XXX Create a string buffer for the whole packet then - # chop it up. Could do some pointer arith here and - # save a bit of data pushing, but my guess is this is - # faster than fudging aruond with ctypes (and easier). - data = ctypes.create_string_buffer(self.length) - ctypes.memmove(data, self.data, self.length) - self.data = data self.data = self.data[num_bytes:] self.length -= num_bytes - self.duration -= num_bytes / float(audio_format.bytes_per_second) - self.timestamp += num_bytes / float(audio_format.bytes_per_second) + self.duration -= num_bytes / audio_format.bytes_per_second + self.timestamp += num_bytes / audio_format.bytes_per_second def get_string_data(self): """Return data as a bytestring. Returns: - bytes or str: Data as a (byte)string. For Python 3 it's a - bytestring while for Python 2 it's a string. + bytes: Data as a (byte)string. """ - # PYTHON2 - remove old Python 2 type checks if self.data is None: return b'' - if isinstance(self.data, bytes_type): - return self.data - - buf = ctypes.create_string_buffer(self.length) - ctypes.memmove(buf, self.data, self.length) - return buf.raw + return memoryview(self.data).tobytes()[:self.length] -class SourceInfo(object): +class SourceInfo: """Source metadata information. Fields are the empty string or zero if the information is not available. @@ -229,7 +212,7 @@ genre = '' -class Source(object): +class Source: """An audio and/or video source. Args: @@ -350,6 +333,43 @@ """ pass + def save(self, filename, file=None, encoder=None): + """Save this Source to a file. + + :Parameters: + `filename` : str + Used to set the file format, and to open the output file + if `file` is unspecified. + `file` : file-like object or None + File to write audio data to. + `encoder` : MediaEncoder or None + If unspecified, all encoders matching the filename extension + are tried. If all fail, the exception from the first one + attempted is raised. + + """ + if not file: + file = open(filename, 'wb') + + if encoder: + encoder.encode(self, file, filename) + else: + first_exception = None + for encoder in pyglet.media.get_encoders(filename): + + try: + encoder.encode(self, file, filename) + return + except MediaEncodeException as e: + first_exception = first_exception or e + file.seek(0) + + if not first_exception: + raise MediaEncodeException(f"No Encoders are available for this extension: '{filename}'") + raise first_exception + + file.close() + # Internal methods that Player calls on the source: def seek(self, timestamp): @@ -368,11 +388,11 @@ """ return self - def get_audio_data(self, bytes, compensation_time=0.0): + def get_audio_data(self, num_bytes, compensation_time=0.0): """Get next packet of audio data. Args: - bytes (int): Maximum number of bytes of data to return. + num_bytes (int): Maximum number of bytes of data to return. compensation_time (float): Time in sec to compensate due to a difference between the master clock and the audio clock. @@ -411,7 +431,7 @@ :class:`.Source` """ if self.is_player_source: - raise MediaException('This source is already a source on a player.') + raise MediaException('This source is already queued on a player.') self.is_player_source = True return self @@ -436,8 +456,7 @@ def __init__(self, source): source = source.get_queue_source() if source.video_format: - raise NotImplementedError( - 'Static sources not supported for video yet.') + raise NotImplementedError('Static sources not supported for video.') self.audio_format = source.audio_format if not self.audio_format: @@ -450,7 +469,7 @@ # Naive implementation. Driver-specific implementations may override # to load static audio data into device (or at least driver) memory. - data = BytesIO() + data = io.BytesIO() while True: audio_data = source.get_audio_data(buffer_size) if not audio_data: @@ -458,14 +477,13 @@ data.write(audio_data.get_string_data()) self._data = data.getvalue() - self._duration = (len(self._data) / - float(self.audio_format.bytes_per_second)) + self._duration = len(self._data) / self.audio_format.bytes_per_second def get_queue_source(self): if self._data is not None: return StaticMemorySource(self._data, self.audio_format) - def get_audio_data(self, bytes, compensation_time=0.0): + def get_audio_data(self, num_bytes, compensation_time=0.0): """The StaticSource does not provide audio data. When the StaticSource is queued on a @@ -492,7 +510,7 @@ def __init__(self, data, audio_format): """Construct a memory source over the given data buffer.""" - self._file = BytesIO(data) + self._file = io.BytesIO(data) self._max_offset = len(data) self.audio_format = audio_format self._duration = len(data) / float(audio_format.bytes_per_second) @@ -513,11 +531,11 @@ self._file.seek(offset) - def get_audio_data(self, bytes, compensation_time=0.0): + def get_audio_data(self, num_bytes, compensation_time=0.0): """Get next packet of audio data. Args: - bytes (int): Maximum number of bytes of data to return. + num_bytes (int): Maximum number of bytes of data to return. compensation_time (float): Not used in this class. Returns: @@ -529,11 +547,11 @@ # Align to sample size if self.audio_format.bytes_per_sample == 2: - bytes &= 0xfffffffe + num_bytes &= 0xfffffffe elif self.audio_format.bytes_per_sample == 4: - bytes &= 0xfffffffc + num_bytes &= 0xfffffffc - data = self._file.read(bytes) + data = self._file.read(num_bytes) if not len(data): return None @@ -541,7 +559,7 @@ return AudioData(data, len(data), timestamp, duration, []) -class SourceGroup(object): +class SourceGroup: """Group of like sources to allow gapless playback. Seamlessly read data from a group of sources to allow for diff -Nru pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libavcodec.py pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libavcodec.py --- pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libavcodec.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libavcodec.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,13 +32,14 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for include/libavcodec/avcodec.h -''' -from __future__ import unicode_literals -from ctypes import (c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64, - c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p, - c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union, - create_string_buffer, memmove) + +"""Wrapper for include/libavcodec/avcodec.h +""" + +from ctypes import c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64 +from ctypes import c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p +from ctypes import c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union +from ctypes import create_string_buffer, memmove import pyglet import pyglet.lib @@ -52,14 +53,18 @@ FF_INPUT_BUFFER_PADDING_SIZE = 32 + class AVPacketSideData(Structure): _fields_ = [ ('data', POINTER(c_uint8)), ('size', c_int), ('type', c_int) ] + + AVBufferRef = libavutil.AVBufferRef + class AVPacket(Structure): _fields_ = [ ('buf', POINTER(AVBufferRef)), @@ -73,11 +78,17 @@ ('side_data_elems', c_int), ('duration', c_int64), ('pos', c_int64), - ('convergence_duration', c_int64) #Deprecated + ('convergence_duration', c_int64) # Deprecated ] -class AVCodecParserContext(Structure): pass + + +class AVCodecParserContext(Structure): + pass + AVRational = libavutil.AVRational + + class AVCodecParameters(Structure): _fields_ = [ ('codec_type', c_int), @@ -110,11 +121,15 @@ ('trailing_padding', c_int), ('seek_preroll', c_int), ] + + class AVProfile(Structure): _fields_ = [ ('profile', c_int), ('name', c_char_p), ] + + class AVCodecDescriptor(Structure): _fields_ = [ ('id', c_int), @@ -125,7 +140,12 @@ ('mime_types', c_char_p), ('profiles', POINTER(AVProfile)) ] -class AVCodecInternal(Structure): pass + + +class AVCodecInternal(Structure): + pass + + class AVCodec(Structure): _fields_ = [ ('name', c_char_p), @@ -141,218 +161,226 @@ ('max_lowres', c_uint8), # And more... ] -class AVCodecContext(Structure): pass -class RcOverride(Structure): pass -class AVHWAccel(Structure): pass + + +class AVCodecContext(Structure): + pass + + +class RcOverride(Structure): + pass + + +class AVHWAccel(Structure): + pass + + AVClass = libavutil.AVClass AVFrame = libavutil.AVFrame AV_NUM_DATA_POINTERS = libavutil.AV_NUM_DATA_POINTERS AVCodecContext._fields_ = [ - ('av_class', POINTER(AVClass)), - ('log_level_offset', c_int), - ('codec_type', c_int), - ('codec', POINTER(AVCodec)), - ('codec_id', c_int), - ('codec_tag', c_uint), - ('priv_data', c_void_p), - ('internal', POINTER(AVCodecInternal)), - ('opaque', c_void_p), - ('bit_rate', c_int64), - ('bit_rate_tolerance', c_int), - ('global_quality', c_int), - ('compression_level', c_int), - ('flags', c_int), - ('flags2', c_int), - ('extradata', POINTER(c_uint8)), - ('extradata_size', c_int), - ('time_base', AVRational), - ('ticks_per_frame', c_int), - ('delay', c_int), - ('width', c_int), - ('height', c_int), - ('coded_width', c_int), - ('coded_height', c_int), - ('gop_size', c_int), - ('pix_fmt', c_int), - ('draw_horiz_band', CFUNCTYPE(None, - POINTER(AVCodecContext), POINTER(AVFrame), - c_int*8, c_int, c_int, c_int)), - ('get_format', CFUNCTYPE(c_int, - POINTER(AVCodecContext), POINTER(c_int))), - ('max_b_frames', c_int), - ('b_quant_factor', c_float), - ('b_frame_strategy', c_int), #Deprecated - ('b_quant_offset', c_float), - ('has_b_frames', c_int), - ('mpeg_quant', c_int), #Deprecated - ('i_quant_factor', c_float), - ('i_quant_offset', c_float), - ('lumi_masking', c_float), - ('temporal_cplx_masking', c_float), - ('spatial_cplx_masking', c_float), - ('p_masking', c_float), - ('dark_masking', c_float), - ('slice_count', c_int), - ('prediction_method', c_int), #Deprecated - ('slice_offset', POINTER(c_int)), - ('sample_aspect_ratio', AVRational), - ('me_cmp', c_int), - ('me_sub_cmp', c_int), - ('mb_cmp', c_int), - ('ildct_cmp', c_int), - ('dia_size', c_int), - ('last_predictor_count', c_int), - ('pre_me', c_int), #Deprecated - ('me_pre_cmp', c_int), - ('pre_dia_size', c_int), - ('me_subpel_quality', c_int), - ('me_range', c_int), - ('slice_flags', c_int), - ('mb_decision', c_int), - ('intra_matrix', POINTER(c_uint16)), - ('inter_matrix', POINTER(c_uint16)), - ('scenechange_threshold', c_int), #Deprecated - ('noise_reduction', c_int), #Deprecated - ('intra_dc_precision', c_int), - ('skip_top', c_int), - ('skip_bottom', c_int), - ('mb_lmin', c_int), - ('mb_lmax', c_int), - ('me_penalty_compensation', c_int), #Deprecated - ('bidir_refine', c_int), - ('brd_scale', c_int), #Deprecated - ('keyint_min', c_int), - ('refs', c_int), - ('chromaoffset', c_int), #Deprecated - ('mv0_threshold', c_int), - ('b_sensitivity', c_int), #Deprecated - ('color_primaries', c_int), - ('color_trc', c_int), - ('colorspace', c_int), - ('color_range', c_int), - ('chroma_sample_location', c_int), - ('slices', c_int), - ('field_order', c_int), - ('sample_rate', c_int), - ('channels', c_int), - ('sample_fmt', c_int), - ('frame_size', c_int), - ('frame_number', c_int), - ('block_align', c_int), - ('cutoff', c_int), - ('channel_layout', c_uint64), - ('request_channel_layout', c_uint64), - ('audio_service_type', c_int), - ('request_sample_fmt', c_int), - ('get_buffer2', CFUNCTYPE(c_int, - POINTER(AVCodecContext), POINTER(AVFrame), c_int)), - ('refcounted_frames', c_int), #Deprecated - ('qcompress', c_float), - ('qblur', c_float), - ('qmin', c_int), - ('qmax', c_int), - ('max_qdiff', c_int), - ('rc_buffer_size', c_int), - ('rc_override_count', c_int), - ('rc_override', POINTER(RcOverride)), - ('rc_max_rate', c_int64), - ('rc_min_rate', c_int64), - ('rc_max_available_vbv_use', c_float), - ('rc_min_vbv_overflow_use', c_float), - ('rc_initial_buffer_occupancy', c_int), - ('coder_type', c_int), #Deprecated - ('context_model', c_int), #Deprecated - ('frame_skip_threshold', c_int), #Deprecated - ('frame_skip_factor', c_int), #Deprecated - ('frame_skip_exp', c_int), #Deprecated - ('frame_skip_cmp', c_int), #Deprecated - ('trellis', c_int), - ('min_prediction_order', c_int), #Deprecated - ('max_prediction_order', c_int), #Deprecated - ('timecode_frame_start', c_int64), #Deprecated - ('rtp_callback', CFUNCTYPE(None, #Deprecated - POINTER(AVCodecContext), c_void_p, c_int, c_int)), - ('rtp_payload_size', c_int), #Deprecated - ('mv_bits', c_int), #Deprecated - ('header_bits', c_int), #Deprecated - ('i_tex_bits', c_int), #Deprecated - ('p_tex_bits', c_int), #Deprecated - ('i_count', c_int), #Deprecated - ('p_count', c_int), #Deprecated - ('skip_count', c_int), #Deprecated - ('misc_bits', c_int), #Deprecated - ('frame_bits', c_int), #Deprecated - ('stats_out', c_char_p), - ('stats_in', c_char_p), - ('workaround_bugs', c_int), - ('strict_std_compliance', c_int), - ('error_concealment', c_int), - ('debug', c_int), - ('err_recognition', c_int), - ('reordered_opaque', c_int64), - ('hwaccel', POINTER(AVHWAccel)), - ('hwaccel_context', c_void_p), - ('error', c_uint64 * AV_NUM_DATA_POINTERS), - ('dct_algo', c_int), - ('idct_algo', c_int), - ('bits_per_coded_sample', c_int), - ('bits_per_raw_sample', c_int), - ('lowres', c_int), - ('coded_frame', POINTER(AVFrame)), #Deprecated - ('thread_count', c_int), - ('thread_type', c_int), - ('active_thread_type', c_int), - ('thread_safe_callbacks', c_int), - ('execute', CFUNCTYPE(c_int, - POINTER(AVCodecContext), - CFUNCTYPE(c_int, POINTER(AVCodecContext), c_void_p), - c_void_p, c_int, c_int, c_int)), - ('execute2', CFUNCTYPE(c_int, - POINTER(AVCodecContext), - CFUNCTYPE(c_int, POINTER(AVCodecContext), c_void_p, c_int, c_int), - c_void_p, c_int, c_int)), - ('nsse_weight', c_int), - ('profile', c_int), - ('level', c_int), - ('skip_loop_filter', c_int), - ('skip_idct', c_int), - ('skip_frame', c_int), - ('subtitle_header', POINTER(c_uint8)), - ('subtitle_header_size', c_int), - ('vbv_delay', c_uint64), #Deprecated - ('side_data_only_packets', c_int), #Deprecated - ('initial_padding', c_int), - ('framerate', AVRational), - #! - ('sw_pix_fmt', c_int), - ('pkt_timebase', AVRational), - ('codec_dexcriptor', AVCodecDescriptor), - ('pts_correction_num_faulty_pts', c_int64), - ('pts_correction_num_faulty_dts', c_int64), - ('pts_correction_last_pts', c_int64), - ('pts_correction_last_dts', c_int64), - ('sub_charenc', c_char_p), - ('sub_charenc_mode', c_int), - ('skip_alpha', c_int), - ('seek_preroll', c_int), - ('debug_mv', c_int), - ('chroma_intra_matrix', POINTER(c_uint16)), - ('dump_separator', POINTER(c_uint8)), - ('codec_whitelist', c_char_p), - ('properties', c_uint), - ('coded_side_data', POINTER(AVPacketSideData)), - ('nb_coded_side_data', c_int), - ('hw_frames_ctx', POINTER(AVBufferRef)), - ('sub_text_format', c_int), - ('trailing_padding', c_int), - ('max_pixels', c_int64), - ('hw_device_ctx', POINTER(AVBufferRef)), - ('hwaccel_flags', c_int), - ('apply_cropping', c_int), - ('extra_hw_frames', c_int) - - - ] + ('av_class', POINTER(AVClass)), + ('log_level_offset', c_int), + ('codec_type', c_int), + ('codec', POINTER(AVCodec)), + ('codec_id', c_int), + ('codec_tag', c_uint), + ('priv_data', c_void_p), + ('internal', POINTER(AVCodecInternal)), + ('opaque', c_void_p), + ('bit_rate', c_int64), + ('bit_rate_tolerance', c_int), + ('global_quality', c_int), + ('compression_level', c_int), + ('flags', c_int), + ('flags2', c_int), + ('extradata', POINTER(c_uint8)), + ('extradata_size', c_int), + ('time_base', AVRational), + ('ticks_per_frame', c_int), + ('delay', c_int), + ('width', c_int), + ('height', c_int), + ('coded_width', c_int), + ('coded_height', c_int), + ('gop_size', c_int), + ('pix_fmt', c_int), + ('draw_horiz_band', CFUNCTYPE(None, + POINTER(AVCodecContext), POINTER(AVFrame), + c_int * 8, c_int, c_int, c_int)), + ('get_format', CFUNCTYPE(c_int, POINTER(AVCodecContext), POINTER(c_int))), + ('max_b_frames', c_int), + ('b_quant_factor', c_float), + ('b_frame_strategy', c_int), # Deprecated + ('b_quant_offset', c_float), + ('has_b_frames', c_int), + ('mpeg_quant', c_int), # Deprecated + ('i_quant_factor', c_float), + ('i_quant_offset', c_float), + ('lumi_masking', c_float), + ('temporal_cplx_masking', c_float), + ('spatial_cplx_masking', c_float), + ('p_masking', c_float), + ('dark_masking', c_float), + ('slice_count', c_int), + ('prediction_method', c_int), # Deprecated + ('slice_offset', POINTER(c_int)), + ('sample_aspect_ratio', AVRational), + ('me_cmp', c_int), + ('me_sub_cmp', c_int), + ('mb_cmp', c_int), + ('ildct_cmp', c_int), + ('dia_size', c_int), + ('last_predictor_count', c_int), + ('pre_me', c_int), # Deprecated + ('me_pre_cmp', c_int), + ('pre_dia_size', c_int), + ('me_subpel_quality', c_int), + ('me_range', c_int), + ('slice_flags', c_int), + ('mb_decision', c_int), + ('intra_matrix', POINTER(c_uint16)), + ('inter_matrix', POINTER(c_uint16)), + ('scenechange_threshold', c_int), # Deprecated + ('noise_reduction', c_int), # Deprecated + ('intra_dc_precision', c_int), + ('skip_top', c_int), + ('skip_bottom', c_int), + ('mb_lmin', c_int), + ('mb_lmax', c_int), + ('me_penalty_compensation', c_int), # Deprecated + ('bidir_refine', c_int), + ('brd_scale', c_int), # Deprecated + ('keyint_min', c_int), + ('refs', c_int), + ('chromaoffset', c_int), # Deprecated + ('mv0_threshold', c_int), + ('b_sensitivity', c_int), # Deprecated + ('color_primaries', c_int), + ('color_trc', c_int), + ('colorspace', c_int), + ('color_range', c_int), + ('chroma_sample_location', c_int), + ('slices', c_int), + ('field_order', c_int), + ('sample_rate', c_int), + ('channels', c_int), + ('sample_fmt', c_int), + ('frame_size', c_int), + ('frame_number', c_int), + ('block_align', c_int), + ('cutoff', c_int), + ('channel_layout', c_uint64), + ('request_channel_layout', c_uint64), + ('audio_service_type', c_int), + ('request_sample_fmt', c_int), + ('get_buffer2', CFUNCTYPE(c_int, POINTER(AVCodecContext), POINTER(AVFrame), c_int)), + ('refcounted_frames', c_int), # Deprecated + ('qcompress', c_float), + ('qblur', c_float), + ('qmin', c_int), + ('qmax', c_int), + ('max_qdiff', c_int), + ('rc_buffer_size', c_int), + ('rc_override_count', c_int), + ('rc_override', POINTER(RcOverride)), + ('rc_max_rate', c_int64), + ('rc_min_rate', c_int64), + ('rc_max_available_vbv_use', c_float), + ('rc_min_vbv_overflow_use', c_float), + ('rc_initial_buffer_occupancy', c_int), + ('coder_type', c_int), # Deprecated + ('context_model', c_int), # Deprecated + ('frame_skip_threshold', c_int), # Deprecated + ('frame_skip_factor', c_int), # Deprecated + ('frame_skip_exp', c_int), # Deprecated + ('frame_skip_cmp', c_int), # Deprecated + ('trellis', c_int), + ('min_prediction_order', c_int), # Deprecated + ('max_prediction_order', c_int), # Deprecated + ('timecode_frame_start', c_int64), # Deprecated + ('rtp_callback', CFUNCTYPE(None, # Deprecated + POINTER(AVCodecContext), c_void_p, c_int, c_int)), + ('rtp_payload_size', c_int), # Deprecated + ('mv_bits', c_int), # Deprecated + ('header_bits', c_int), # Deprecated + ('i_tex_bits', c_int), # Deprecated + ('p_tex_bits', c_int), # Deprecated + ('i_count', c_int), # Deprecated + ('p_count', c_int), # Deprecated + ('skip_count', c_int), # Deprecated + ('misc_bits', c_int), # Deprecated + ('frame_bits', c_int), # Deprecated + ('stats_out', c_char_p), + ('stats_in', c_char_p), + ('workaround_bugs', c_int), + ('strict_std_compliance', c_int), + ('error_concealment', c_int), + ('debug', c_int), + ('err_recognition', c_int), + ('reordered_opaque', c_int64), + ('hwaccel', POINTER(AVHWAccel)), + ('hwaccel_context', c_void_p), + ('error', c_uint64 * AV_NUM_DATA_POINTERS), + ('dct_algo', c_int), + ('idct_algo', c_int), + ('bits_per_coded_sample', c_int), + ('bits_per_raw_sample', c_int), + ('lowres', c_int), + ('coded_frame', POINTER(AVFrame)), # Deprecated + ('thread_count', c_int), + ('thread_type', c_int), + ('active_thread_type', c_int), + ('thread_safe_callbacks', c_int), + ('execute', CFUNCTYPE(c_int, + POINTER(AVCodecContext), + CFUNCTYPE(c_int, POINTER(AVCodecContext), c_void_p), + c_void_p, c_int, c_int, c_int)), + ('execute2', CFUNCTYPE(c_int, + POINTER(AVCodecContext), + CFUNCTYPE(c_int, POINTER(AVCodecContext), c_void_p, c_int, c_int), + c_void_p, c_int, c_int)), + ('nsse_weight', c_int), + ('profile', c_int), + ('level', c_int), + ('skip_loop_filter', c_int), + ('skip_idct', c_int), + ('skip_frame', c_int), + ('subtitle_header', POINTER(c_uint8)), + ('subtitle_header_size', c_int), + ('vbv_delay', c_uint64), # Deprecated + ('side_data_only_packets', c_int), # Deprecated + ('initial_padding', c_int), + ('framerate', AVRational), + # ! + ('sw_pix_fmt', c_int), + ('pkt_timebase', AVRational), + ('codec_dexcriptor', AVCodecDescriptor), + ('pts_correction_num_faulty_pts', c_int64), + ('pts_correction_num_faulty_dts', c_int64), + ('pts_correction_last_pts', c_int64), + ('pts_correction_last_dts', c_int64), + ('sub_charenc', c_char_p), + ('sub_charenc_mode', c_int), + ('skip_alpha', c_int), + ('seek_preroll', c_int), + ('debug_mv', c_int), + ('chroma_intra_matrix', POINTER(c_uint16)), + ('dump_separator', POINTER(c_uint8)), + ('codec_whitelist', c_char_p), + ('properties', c_uint), + ('coded_side_data', POINTER(AVPacketSideData)), + ('nb_coded_side_data', c_int), + ('hw_frames_ctx', POINTER(AVBufferRef)), + ('sub_text_format', c_int), + ('trailing_padding', c_int), + ('max_pixels', c_int64), + ('hw_device_ctx', POINTER(AVBufferRef)), + ('hwaccel_flags', c_int), + ('apply_cropping', c_int), + ('extra_hw_frames', c_int) + +] AV_CODEC_ID_VP8 = 139 AV_CODEC_ID_VP9 = 167 @@ -367,36 +395,36 @@ AVDictionary = libavutil.AVDictionary avcodec.avcodec_open2.restype = c_int avcodec.avcodec_open2.argtypes = [POINTER(AVCodecContext), - POINTER(AVCodec), - POINTER(POINTER(AVDictionary))] + POINTER(AVCodec), + POINTER(POINTER(AVDictionary))] avcodec.avcodec_free_context.argtypes = [POINTER(POINTER(AVCodecContext))] avcodec.av_packet_alloc.restype = POINTER(AVPacket) avcodec.av_init_packet.argtypes = [POINTER(AVPacket)] avcodec.avcodec_decode_audio4.restype = c_int avcodec.avcodec_decode_audio4.argtypes = [POINTER(AVCodecContext), - POINTER(AVFrame), POINTER(c_int), - POINTER(AVPacket)] + POINTER(AVFrame), POINTER(c_int), + POINTER(AVPacket)] avcodec.avcodec_decode_video2.restype = c_int avcodec.avcodec_decode_video2.argtypes = [POINTER(AVCodecContext), - POINTER(AVFrame), POINTER(c_int), - POINTER(AVPacket)] + POINTER(AVFrame), POINTER(c_int), + POINTER(AVPacket)] avcodec.avcodec_flush_buffers.argtypes = [POINTER(AVCodecContext)] avcodec.avcodec_alloc_context3.restype = POINTER(AVCodecContext) avcodec.avcodec_alloc_context3.argtypes = [POINTER(AVCodec)] avcodec.avcodec_free_context.argtypes = [POINTER(POINTER(AVCodecContext))] avcodec.avcodec_parameters_to_context.restype = c_int avcodec.avcodec_parameters_to_context.argtypes = [POINTER(AVCodecContext), - POINTER(AVCodecParameters)] + POINTER(AVCodecParameters)] avcodec.avcodec_get_name.restype = c_char_p avcodec.avcodec_get_name.argtypes = [c_int] avcodec.avcodec_find_decoder_by_name.restype = POINTER(AVCodec) avcodec.avcodec_find_decoder_by_name.argtypes = [c_char_p] __all__ = [ -'avcodec', -'FF_INPUT_BUFFER_PADDING_SIZE', -'AVPacket', -'AVCodecContext', -'AV_CODEC_ID_VP8', -'AV_CODEC_ID_VP9', + 'avcodec', + 'FF_INPUT_BUFFER_PADDING_SIZE', + 'AVPacket', + 'AVCodecContext', + 'AV_CODEC_ID_VP8', + 'AV_CODEC_ID_VP9', ] diff -Nru pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libavformat.py pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libavformat.py --- pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libavformat.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libavformat.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,13 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for include/libavformat/avformat.h -''' -from __future__ import unicode_literals -from ctypes import (c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64, - c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p, - c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union, - create_string_buffer, memmove) +"""Wrapper for include/libavformat/avformat.h +""" +from ctypes import c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64 +from ctypes import c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p +from ctypes import c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union +from ctypes import create_string_buffer, memmove import pyglet import pyglet.lib @@ -51,38 +50,54 @@ darwin='avformat.58' ) -AVSEEK_FLAG_BACKWARD = 1 # ///< seek backward -AVSEEK_FLAG_BYTE = 2 # ///< seeking based on position in bytes -AVSEEK_FLAG_ANY = 4 # ///< seek to any frame, even non-keyframes -AVSEEK_FLAG_FRAME = 8 # ///< seeking based on frame number +AVSEEK_FLAG_BACKWARD = 1 # ///< seek backward +AVSEEK_FLAG_BYTE = 2 # ///< seeking based on position in bytes +AVSEEK_FLAG_ANY = 4 # ///< seek to any frame, even non-keyframes +AVSEEK_FLAG_FRAME = 8 # ///< seeking based on frame number MAX_REORDER_DELAY = 16 + class AVPacketList(Structure): pass -class AVInputFormat(Structure): + + +class AVInputFormat(Structure): _fields_ = [ ('name', c_char_p) ] -class AVOutputFormat(Structure): pass -class AVIOContext(Structure): pass -class AVIndexEntry(Structure): pass + + +class AVOutputFormat(Structure): + pass + + +class AVIOContext(Structure): + pass + + +class AVIndexEntry(Structure): + pass + + class AVStreamInfo(Structure): _fields_ = [ ('last_dts', c_int64), ('duration_gcd', c_int64), ('duration_count', c_int), ('rfps_duration_sum', c_int64), - ('duration_error', POINTER(c_double * 2 * (30*12+30+3+6))), - ('codec_info_duration', c_int64), - ('codec_info_duration_fields', c_int64), - ('frame_delay_evidence', c_int), - ('found_decoder', c_int), - ('last_duration', c_int64), - ('fps_first_dts', c_int64), - ('fps_first_dts_idx', c_int), - ('fps_last_dts', c_int64), - ('fps_last_dts_idx', c_int), - ] + ('duration_error', POINTER(c_double * 2 * (30 * 12 + 30 + 3 + 6))), + ('codec_info_duration', c_int64), + ('codec_info_duration_fields', c_int64), + ('frame_delay_evidence', c_int), + ('found_decoder', c_int), + ('last_duration', c_int64), + ('fps_first_dts', c_int64), + ('fps_first_dts_idx', c_int), + ('fps_last_dts', c_int64), + ('fps_last_dts_idx', c_int), + ] + + class AVProbeData(Structure): _fields_ = [ ('filename', c_char_p), @@ -90,8 +105,16 @@ ('buf_size', c_int), ('mime_type', c_char_p) ] -class FFFrac(Structure): pass -class AVStreamInternal(Structure): pass + + +class FFFrac(Structure): + pass + + +class AVStreamInternal(Structure): + pass + + class AVFrac(Structure): _fields_ = [ ('val', c_int64), @@ -99,6 +122,7 @@ ('den', c_int64), ] + AVCodecContext = libavcodec.AVCodecContext AVPacketSideData = libavcodec.AVPacketSideData AVPacket = libavcodec.AVPacket @@ -107,6 +131,8 @@ AVRational = libavutil.AVRational AVDictionary = libavutil.AVDictionary AVFrame = libavutil.AVFrame + + class AVStream(Structure): _fields_ = [ ('index', c_int), @@ -141,7 +167,7 @@ ('parser', POINTER(AVCodecParserContext)), ('last_in_packet_buffer', POINTER(AVPacketList)), ('probe_data', AVProbeData), - ('pts_buffer', c_int64 * (MAX_REORDER_DELAY+1)), + ('pts_buffer', c_int64 * (MAX_REORDER_DELAY + 1)), ('index_entries', POINTER(AVIndexEntry)), ('nb_index_entries', c_int), ('index_entries_allocated_size', c_uint), @@ -159,8 +185,8 @@ ('pts_wrap_reference', c_int64), ('pts_wrap_behavior', c_int), ('update_initial_durations_done', c_int), - ('pts_reorder_error', c_int64 * (MAX_REORDER_DELAY+1)), - ('pts_reorder_error_count', c_uint8 * (MAX_REORDER_DELAY+1)), + ('pts_reorder_error', c_int64 * (MAX_REORDER_DELAY + 1)), + ('pts_reorder_error_count', c_uint8 * (MAX_REORDER_DELAY + 1)), ('last_dts_for_order_check', c_int64), ('dts_ordered', c_uint8), ('dts_misordered', c_uint8), @@ -168,135 +194,152 @@ ('display_aspect_ratio', AVRational), ('internal', POINTER(AVStreamInternal)) ] -class AVProgram(Structure): pass -class AVChapter(Structure): pass -class AVFormatInternal(Structure): pass + + +class AVProgram(Structure): + pass + + +class AVChapter(Structure): + pass + + +class AVFormatInternal(Structure): + pass + + class AVIOInterruptCB(Structure): _fields_ = [ ('callback', CFUNCTYPE(c_int, c_void_p)), ('opaque', c_void_p) ] + AVClass = libavutil.AVClass AVCodec = libavcodec.AVCodec -class AVFormatContext(Structure): pass + + +class AVFormatContext(Structure): + pass + + AVFormatContext._fields_ = [ - ('av_class', POINTER(AVClass)), - ('iformat', POINTER(AVInputFormat)), - ('oformat', POINTER(AVOutputFormat)), - ('priv_data', c_void_p), - ('pb', POINTER(AVIOContext)), - ('ctx_flags', c_int), - ('nb_streams', c_uint), - ('streams', POINTER(POINTER(AVStream))), - ('filename', c_char*1024), #Deprecated - ('url', c_char_p), - ('start_time', c_int64), - ('duration', c_int64), - ('bit_rate', c_int64), - ('packet_size', c_uint), - ('max_delay', c_int), - ('flags', c_int), - ('probesize', c_int64), - ('max_analyze_duration', c_int64), - ('key', POINTER(c_uint8)), - ('keylen', c_int), - ('nb_programs', c_uint), - ('programs', POINTER(POINTER(AVProgram))), - ('video_codec_id', c_int), - ('audio_codec_id', c_int), - ('subtitle_codec_id', c_int), - ('max_index_size', c_uint), - ('max_picture_buffer', c_uint), - ('nb_chapters', c_uint), - ('chapters', POINTER(POINTER(AVChapter))), - ('metadata', POINTER(AVDictionary)), - ('start_time_realtime', c_int64), - ('fps_probe_size', c_int), - ('error_recognition', c_int), - ('interrupt_callback', AVIOInterruptCB), - ('debug', c_int), - ('max_interleave_delta', c_int64), - ('strict_std_compliance', c_int), - ('event_flags', c_int), - ('max_ts_probe', c_int), - ('avoid_negative_ts', c_int), - ('ts_id', c_int), - ('audio_preload', c_int), - ('max_chunk_duration', c_int), - ('max_chunk_size', c_int), - ('use_wallclock_as_timestamps', c_int), - ('avio_flags', c_int), - ('duration_estimation_method', c_uint), - ('skip_initial_bytes', c_int64), - ('correct_ts_overflow', c_uint), - ('seek2any', c_int), - ('flush_packets', c_int), - ('probe_score', c_int), - ('format_probesize', c_int), - ('codec_whitelist', c_char_p), - ('format_whitelist', c_char_p), - ('internal', POINTER(AVFormatInternal)), - ('io_repositioned', c_int), - ('video_codec', POINTER(AVCodec)), - ('audio_codec', POINTER(AVCodec)), - ('subtitle_codec', POINTER(AVCodec)), - ('data_codec', POINTER(AVCodec)), - ('metadata_header_padding', c_int), - ('opaque', c_void_p), - ('control_message_cb', CFUNCTYPE(c_int, - POINTER(AVFormatContext), c_int, c_void_p, - c_size_t)), - ('output_ts_offset', c_int64), - ('dump_separator', POINTER(c_uint8)), - ('data_codec_id', c_int), - #! one more in here? - ('protocol_whitelist', c_char_p), - ('io_open', CFUNCTYPE(c_int, - POINTER(AVFormatContext), - POINTER(POINTER(AVIOContext)), - c_char_p, c_int, - POINTER(POINTER(AVDictionary)))), - ('io_close', CFUNCTYPE(None, - POINTER(AVFormatContext), POINTER(AVIOContext))), - ('protocol_blacklist', c_char_p), - ('max_streams', c_int) - ] + ('av_class', POINTER(AVClass)), + ('iformat', POINTER(AVInputFormat)), + ('oformat', POINTER(AVOutputFormat)), + ('priv_data', c_void_p), + ('pb', POINTER(AVIOContext)), + ('ctx_flags', c_int), + ('nb_streams', c_uint), + ('streams', POINTER(POINTER(AVStream))), + ('filename', c_char * 1024), # Deprecated + ('url', c_char_p), + ('start_time', c_int64), + ('duration', c_int64), + ('bit_rate', c_int64), + ('packet_size', c_uint), + ('max_delay', c_int), + ('flags', c_int), + ('probesize', c_int64), + ('max_analyze_duration', c_int64), + ('key', POINTER(c_uint8)), + ('keylen', c_int), + ('nb_programs', c_uint), + ('programs', POINTER(POINTER(AVProgram))), + ('video_codec_id', c_int), + ('audio_codec_id', c_int), + ('subtitle_codec_id', c_int), + ('max_index_size', c_uint), + ('max_picture_buffer', c_uint), + ('nb_chapters', c_uint), + ('chapters', POINTER(POINTER(AVChapter))), + ('metadata', POINTER(AVDictionary)), + ('start_time_realtime', c_int64), + ('fps_probe_size', c_int), + ('error_recognition', c_int), + ('interrupt_callback', AVIOInterruptCB), + ('debug', c_int), + ('max_interleave_delta', c_int64), + ('strict_std_compliance', c_int), + ('event_flags', c_int), + ('max_ts_probe', c_int), + ('avoid_negative_ts', c_int), + ('ts_id', c_int), + ('audio_preload', c_int), + ('max_chunk_duration', c_int), + ('max_chunk_size', c_int), + ('use_wallclock_as_timestamps', c_int), + ('avio_flags', c_int), + ('duration_estimation_method', c_uint), + ('skip_initial_bytes', c_int64), + ('correct_ts_overflow', c_uint), + ('seek2any', c_int), + ('flush_packets', c_int), + ('probe_score', c_int), + ('format_probesize', c_int), + ('codec_whitelist', c_char_p), + ('format_whitelist', c_char_p), + ('internal', POINTER(AVFormatInternal)), + ('io_repositioned', c_int), + ('video_codec', POINTER(AVCodec)), + ('audio_codec', POINTER(AVCodec)), + ('subtitle_codec', POINTER(AVCodec)), + ('data_codec', POINTER(AVCodec)), + ('metadata_header_padding', c_int), + ('opaque', c_void_p), + ('control_message_cb', CFUNCTYPE(c_int, + POINTER(AVFormatContext), c_int, c_void_p, + c_size_t)), + ('output_ts_offset', c_int64), + ('dump_separator', POINTER(c_uint8)), + ('data_codec_id', c_int), + # ! one more in here? + ('protocol_whitelist', c_char_p), + ('io_open', CFUNCTYPE(c_int, + POINTER(AVFormatContext), + POINTER(POINTER(AVIOContext)), + c_char_p, c_int, + POINTER(POINTER(AVDictionary)))), + ('io_close', CFUNCTYPE(None, + POINTER(AVFormatContext), POINTER(AVIOContext))), + ('protocol_blacklist', c_char_p), + ('max_streams', c_int) +] avformat.av_register_all.restype = None avformat.av_find_input_format.restype = c_int avformat.av_find_input_format.argtypes = [c_int] avformat.avformat_open_input.restype = c_int avformat.avformat_open_input.argtypes = [ - POINTER(POINTER(AVFormatContext)), - c_char_p, - POINTER(AVInputFormat), - POINTER(POINTER(AVDictionary))] + POINTER(POINTER(AVFormatContext)), + c_char_p, + POINTER(AVInputFormat), + POINTER(POINTER(AVDictionary))] avformat.avformat_find_stream_info.restype = c_int avformat.avformat_find_stream_info.argtypes = [ - POINTER(AVFormatContext), - POINTER(POINTER(AVDictionary))] -avformat.avformat_close_input.restype = None + POINTER(AVFormatContext), + POINTER(POINTER(AVDictionary))] +avformat.avformat_close_input.restype = None avformat.avformat_close_input.argtypes = [ - POINTER(POINTER(AVFormatContext))] + POINTER(POINTER(AVFormatContext))] avformat.av_read_frame.restype = c_int avformat.av_read_frame.argtypes = [POINTER(AVFormatContext), - POINTER(AVPacket)] + POINTER(AVPacket)] avformat.av_seek_frame.restype = c_int avformat.av_seek_frame.argtypes = [POINTER(AVFormatContext), - c_int, c_int64, c_int] + c_int, c_int64, c_int] avformat.avformat_seek_file.restype = c_int avformat.avformat_seek_file.argtypes = [POINTER(AVFormatContext), - c_int, c_int64, c_int64, c_int64, c_int] + c_int, c_int64, c_int64, c_int64, c_int] avformat.av_guess_frame_rate.restype = AVRational -avformat.av_guess_frame_rate.argtypes = [POINTER(AVFormatContext), - POINTER(AVStream), POINTER(AVFrame)] +avformat.av_guess_frame_rate.argtypes = [POINTER(AVFormatContext), + POINTER(AVStream), POINTER(AVFrame)] __all__ = [ -'avformat', -'AVSEEK_FLAG_BACKWARD', -'AVSEEK_FLAG_BYTE', -'AVSEEK_FLAG_ANY', -'AVSEEK_FLAG_FRAME', -'AVFormatContext' + 'avformat', + 'AVSEEK_FLAG_BACKWARD', + 'AVSEEK_FLAG_BYTE', + 'AVSEEK_FLAG_ANY', + 'AVSEEK_FLAG_FRAME', + 'AVFormatContext' ] diff -Nru pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libavutil.py pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libavutil.py --- pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libavutil.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libavutil.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,13 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for include/libavutil/avutil.h -''' -from __future__ import unicode_literals -from ctypes import (c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64, - c_uint8, c_int8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, - c_char_p, c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, - Union, create_string_buffer, memmove) +"""Wrapper for include/libavutil/avutil.h +""" +from ctypes import c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64 +from ctypes import c_uint8, c_int8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char +from ctypes import c_char_p, c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure +from ctypes import Union, create_string_buffer, memmove import pyglet import pyglet.lib @@ -76,13 +75,15 @@ AV_PIX_FMT_ARGB = 25 AV_PIX_FMT_RGBA = 26 + class AVBuffer(Structure): _fields_ = [ ('data', POINTER(c_uint8)), ('size', c_int), - #.. more + # .. more ] + class AVBufferRef(Structure): _fields_ = [ ('buffer', POINTER(AVBuffer)), @@ -96,21 +97,31 @@ ('key', c_char_p), ('value', c_char_p) ] + + class AVDictionary(Structure): _fields_ = [ ('count', c_int), ('elems', POINTER(AVDictionaryEntry)) ] -class AVClass(Structure): pass + +class AVClass(Structure): + pass + + class AVRational(Structure): _fields_ = [ ('num', c_int), ('den', c_int) ] -class AVFrameSideData(Structure): pass -class AVFrame(Structure): + +class AVFrameSideData(Structure): + pass + + +class AVFrame(Structure): _fields_ = [ ('data', POINTER(c_uint8) * AV_NUM_DATA_POINTERS), ('linesize', c_int * AV_NUM_DATA_POINTERS), @@ -123,13 +134,13 @@ ('pict_type', c_int), ('sample_aspect_ratio', AVRational), ('pts', c_int64), - ('pkt_pts', c_int64), #Deprecated + ('pkt_pts', c_int64), # Deprecated ('pkt_dts', c_int64), ('coded_picture_number', c_int), ('display_picture_number', c_int), ('quality', c_int), ('opaque', c_void_p), - ('error', c_uint64 * AV_NUM_DATA_POINTERS), #Deprecated + ('error', c_uint64 * AV_NUM_DATA_POINTERS), # Deprecated ('repeat_pict', c_int), ('interlaced_frame', c_int), ('top_field_first', c_int), @@ -151,38 +162,39 @@ ('best_effort_timestamp', c_int64), ('pkt_pos', c_int64), ('pkt_duration', c_int64), - #! + # ! ('metadata', POINTER(AVDictionary)), ('decode_error_flags', c_int), ('channels', c_int), ('pkt_size', c_int), - ('qscale_table', POINTER(c_int8)), #Deprecated - ('qstride', c_int), #Deprecated - ('qscale_type', c_int), #Deprecated - ('qp_table_buf', POINTER(AVBufferRef)), #Deprecated + ('qscale_table', POINTER(c_int8)), # Deprecated + ('qstride', c_int), # Deprecated + ('qscale_type', c_int), # Deprecated + ('qp_table_buf', POINTER(AVBufferRef)), # Deprecated ('hw_frames_ctx', POINTER(AVBufferRef)), ('opaque_ref', POINTER(AVBufferRef)), - ('crop_top', c_size_t), # video frames only - ('crop_bottom', c_size_t), # video frames only - ('crop_left', c_size_t), # video frames only - ('crop_right', c_size_t), # video frames only + ('crop_top', c_size_t), # video frames only + ('crop_bottom', c_size_t), # video frames only + ('crop_left', c_size_t), # video frames only + ('crop_right', c_size_t), # video frames only ('private_ref', POINTER(AVBufferRef)), ] + + AV_NOPTS_VALUE = -0x8000000000000000 AV_TIME_BASE = 1000000 AV_TIME_BASE_Q = AVRational(1, AV_TIME_BASE) - avutil.av_version_info.restype = c_char_p avutil.av_dict_get.restype = POINTER(AVDictionaryEntry) avutil.av_dict_get.argtypes = [POINTER(AVDictionary), - c_char_p, POINTER(AVDictionaryEntry), - c_int] + c_char_p, POINTER(AVDictionaryEntry), + c_int] avutil.av_rescale_q.restype = c_int64 avutil.av_rescale_q.argtypes = [c_int64, AVRational, AVRational] avutil.av_samples_get_buffer_size.restype = c_int avutil.av_samples_get_buffer_size.argtypes = [POINTER(c_int), - c_int, c_int, c_int] + c_int, c_int, c_int] avutil.av_frame_alloc.restype = POINTER(AVFrame) avutil.av_frame_free.argtypes = [POINTER(POINTER(AVFrame))] avutil.av_get_default_channel_layout.restype = c_int64 @@ -195,7 +207,7 @@ avutil.av_frame_get_best_effort_timestamp.argtypes = [POINTER(AVFrame)] avutil.av_image_fill_arrays.restype = c_int avutil.av_image_fill_arrays.argtypes = [POINTER(c_uint8) * 4, c_int * 4, - POINTER(c_uint8), c_int, c_int, c_int, c_int] + POINTER(c_uint8), c_int, c_int, c_int, c_int] avutil.av_dict_set.restype = c_int avutil.av_dict_set.argtypes = [POINTER(POINTER(AVDictionary)), c_char_p, c_char_p, c_int] @@ -204,34 +216,34 @@ avutil.av_log_set_level.argtypes = [c_uint] __all__ = [ -'avutil', -'AVMEDIA_TYPE_UNKNOWN', -'AVMEDIA_TYPE_VIDEO', -'AVMEDIA_TYPE_AUDIO', -'AVMEDIA_TYPE_DATA', -'AVMEDIA_TYPE_SUBTITLE', -'AVMEDIA_TYPE_ATTACHMENT', -'AVMEDIA_TYPE_NB', -'AV_SAMPLE_FMT_U8', -'AV_SAMPLE_FMT_S16', -'AV_SAMPLE_FMT_S32', -'AV_SAMPLE_FMT_FLT', -'AV_SAMPLE_FORMAT_DOUBLE', -'AV_SAMPLE_FMT_U8P', -'AV_SAMPLE_FMT_S16P', -'AV_SAMPLE_FMT_S32P', -'AV_SAMPLE_FMT_FLTP', -'AV_SAMPLE_FMT_DBLP', -'AV_SAMPLE_FMT_S64', -'AV_SAMPLE_FMT_S64P', -'AV_NUM_DATA_POINTERS', -'AV_PIX_FMT_RGB24', -'AV_PIX_FMT_ARGB', -'AV_PIX_FMT_RGBA', -'AV_NOPTS_VALUE', -'AV_TIME_BASE', -'AV_TIME_BASE_Q', -'AVFrame', -'AVRational', -'AVDictionary', + 'avutil', + 'AVMEDIA_TYPE_UNKNOWN', + 'AVMEDIA_TYPE_VIDEO', + 'AVMEDIA_TYPE_AUDIO', + 'AVMEDIA_TYPE_DATA', + 'AVMEDIA_TYPE_SUBTITLE', + 'AVMEDIA_TYPE_ATTACHMENT', + 'AVMEDIA_TYPE_NB', + 'AV_SAMPLE_FMT_U8', + 'AV_SAMPLE_FMT_S16', + 'AV_SAMPLE_FMT_S32', + 'AV_SAMPLE_FMT_FLT', + 'AV_SAMPLE_FORMAT_DOUBLE', + 'AV_SAMPLE_FMT_U8P', + 'AV_SAMPLE_FMT_S16P', + 'AV_SAMPLE_FMT_S32P', + 'AV_SAMPLE_FMT_FLTP', + 'AV_SAMPLE_FMT_DBLP', + 'AV_SAMPLE_FMT_S64', + 'AV_SAMPLE_FMT_S64P', + 'AV_NUM_DATA_POINTERS', + 'AV_PIX_FMT_RGB24', + 'AV_PIX_FMT_ARGB', + 'AV_PIX_FMT_RGBA', + 'AV_NOPTS_VALUE', + 'AV_TIME_BASE', + 'AV_TIME_BASE_Q', + 'AVFrame', + 'AVRational', + 'AVDictionary', ] diff -Nru pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libswresample.py pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libswresample.py --- pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libswresample.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libswresample.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,13 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for include/libswresample/swresample.h -''' -from __future__ import unicode_literals -from ctypes import (c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64, - c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p, - c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union, - create_string_buffer, memmove) +"""Wrapper for include/libswresample/swresample.h +""" +from ctypes import c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64 +from ctypes import c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p +from ctypes import c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union +from ctypes import create_string_buffer, memmove import pyglet import pyglet.lib @@ -51,29 +50,32 @@ SWR_CH_MAX = 32 -class SwrContext(Structure): pass + +class SwrContext(Structure): + pass + swresample.swr_alloc_set_opts.restype = POINTER(SwrContext) swresample.swr_alloc_set_opts.argtypes = [POINTER(SwrContext), - c_int64, c_int, c_int, c_int64, - c_int, c_int, c_int, c_void_p] + c_int64, c_int, c_int, c_int64, + c_int, c_int, c_int, c_void_p] swresample.swr_init.restype = c_int swresample.swr_init.argtypes = [POINTER(SwrContext)] swresample.swr_free.argtypes = [POINTER(POINTER(SwrContext))] swresample.swr_convert.restype = c_int swresample.swr_convert.argtypes = [POINTER(SwrContext), - POINTER(c_uint8) * SWR_CH_MAX, - c_int, - POINTER(POINTER(c_uint8)), - c_int] + POINTER(c_uint8) * SWR_CH_MAX, + c_int, + POINTER(POINTER(c_uint8)), + c_int] swresample.swr_set_compensation.restype = c_int swresample.swr_set_compensation.argtypes = [POINTER(SwrContext), c_int, c_int] swresample.swr_get_out_samples.restype = c_int -swresample.swr_get_out_samples.argtypes = [POINTER(SwrContext), c_int] +swresample.swr_get_out_samples.argtypes = [POINTER(SwrContext), c_int] __all__ = [ -'swresample', -'SWR_CH_MAX', -'SwrContext' + 'swresample', + 'SWR_CH_MAX', + 'SwrContext' ] diff -Nru pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libswscale.py pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libswscale.py --- pyglet-1.4.10/pyglet/media/codecs/ffmpeg_lib/libswscale.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/ffmpeg_lib/libswscale.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,13 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for include/libswscale/swscale.h -''' -from __future__ import unicode_literals -from ctypes import (c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64, - c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p, - c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union, - create_string_buffer, memmove) +"""Wrapper for include/libswscale/swscale.h +""" +from ctypes import c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64 +from ctypes import c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p +from ctypes import c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union +from ctypes import create_string_buffer, memmove import pyglet import pyglet.lib @@ -51,27 +50,33 @@ SWS_FAST_BILINEAR = 1 -class SwsContext(Structure): pass -class SwsFilter(Structure): pass + +class SwsContext(Structure): + pass + + +class SwsFilter(Structure): + pass + swscale.sws_getCachedContext.restype = POINTER(SwsContext) swscale.sws_getCachedContext.argtypes = [POINTER(SwsContext), - c_int, c_int, c_int, c_int, - c_int, c_int, c_int, - POINTER(SwsFilter), POINTER(SwsFilter), - POINTER(c_double)] + c_int, c_int, c_int, c_int, + c_int, c_int, c_int, + POINTER(SwsFilter), POINTER(SwsFilter), + POINTER(c_double)] swscale.sws_freeContext.argtypes = [POINTER(SwsContext)] swscale.sws_scale.restype = c_int swscale.sws_scale.argtypes = [POINTER(SwsContext), - POINTER(POINTER(c_uint8)), - POINTER(c_int), - c_int, c_int, - POINTER(POINTER(c_uint8)), - POINTER(c_int)] + POINTER(POINTER(c_uint8)), + POINTER(c_int), + c_int, c_int, + POINTER(POINTER(c_uint8)), + POINTER(c_int)] __all__ = [ -'swscale', -'SWS_FAST_BILINEAR', -'SwsContext', -'SwsFilter' + 'swscale', + 'SWS_FAST_BILINEAR', + 'SwsContext', + 'SwsFilter' ] diff -Nru pyglet-1.4.10/pyglet/media/codecs/ffmpeg.py pyglet-1.5.14/pyglet/media/codecs/ffmpeg.py --- pyglet-1.4.10/pyglet/media/codecs/ffmpeg.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/ffmpeg.py 2020-11-16 14:49:52.000000000 +0000 @@ -35,11 +35,8 @@ """Use ffmpeg to decode audio and video media. """ -from __future__ import print_function -from __future__ import division -from builtins import range -from builtins import object +import tempfile from ctypes import (c_int, c_uint16, c_int32, c_int64, c_uint32, c_uint64, c_uint8, c_uint, c_double, c_float, c_ubyte, c_size_t, c_char, c_char_p, c_void_p, addressof, byref, cast, POINTER, CFUNCTYPE, Structure, Union, @@ -50,7 +47,7 @@ import pyglet.lib from pyglet import image -from pyglet.compat import asbytes, asbytes_filename, asstr +from pyglet.util import asbytes, asbytes_filename, asstr from ..events import MediaEvent from ..exceptions import MediaFormatException from .base import StreamingSource, VideoFormat, AudioFormat @@ -59,7 +56,7 @@ from . import MediaEncoder, MediaDecoder -class FileInfo(object): +class FileInfo: def __init__(self): self.n_streams = None self.start_time = None @@ -74,7 +71,7 @@ self.genre = "" -class StreamVideoInfo(object): +class StreamVideoInfo: def __init__(self, width, height, sample_aspect_num, sample_aspect_den, frame_rate_num, frame_rate_den, codec_id): self.width = width @@ -86,7 +83,7 @@ self.codec_id = codec_id -class StreamAudioInfo(object): +class StreamAudioInfo: def __init__(self, sample_format, sample_rate, channels): self.sample_format = sample_format self.sample_rate = sample_rate @@ -140,7 +137,9 @@ None, None) if result != 0: - raise FFmpegException('Error opening file ' + filename.decode("utf8")) + raise FFmpegException('avformat_open_input in ffmpeg_open_filename returned an error opening file ' + + filename.decode("utf8") + + ' Error code: ' + str(result)) result = avformat.avformat_find_stream_info(file.context, None) if result < 0: @@ -423,14 +422,15 @@ return int(timestamp * 1000000) -class _Packet(object): +class _Packet: def __init__(self, packet, timestamp): self.packet = AVPacket() ffmpeg_transfer_packet(byref(self.packet), packet) self.timestamp = timestamp def __del__(self): - ffmpeg_unref_packet(self.packet) + if ffmpeg_unref_packet is not None: + ffmpeg_unref_packet(self.packet) class VideoPacket(_Packet): @@ -454,16 +454,22 @@ SAMPLE_CORRECTION_PERCENT_MAX = 10 def __init__(self, filename, file=None): - if file is not None: - raise NotImplementedError('Loading from file stream is not supported') + self._packet = None + self._video_stream = None + self._audio_stream = None + self._file = None + + if file: + file.seek(0) + self._tempfile = tempfile.NamedTemporaryFile(buffering=False) + self._tempfile.write(file.read()) + filename = self._tempfile.name self._file = ffmpeg_open_filename(asbytes_filename(filename)) if not self._file: raise FFmpegException('Could not open "{0}"'.format(filename)) - self._video_stream = None self._video_stream_index = None - self._audio_stream = None self._audio_stream_index = None self._audio_format = None @@ -573,17 +579,18 @@ self.seek(0.0) def __del__(self): - if _debug: - print('del ffmpeg source') - - ffmpeg_free_packet(self._packet) - if self._video_stream: + if hasattr(self, '_tempfile'): + self._tempfile.close() + if self._packet and ffmpeg_free_packet is not None: + ffmpeg_free_packet(self._packet) + if self._video_stream and swscale is not None: swscale.sws_freeContext(self.img_convert_ctx) ffmpeg_close_stream(self._video_stream) if self._audio_stream: swresample.swr_free(self.audio_convert_ctx) ffmpeg_close_stream(self._audio_stream) - ffmpeg_close_file(self._file) + if self._file and ffmpeg_close_file is not None: + ffmpeg_close_file(self._file) def seek(self, timestamp): if _debug: diff -Nru pyglet-1.4.10/pyglet/media/codecs/gstreamer.py pyglet-1.5.14/pyglet/media/codecs/gstreamer.py --- pyglet-1.4.10/pyglet/media/codecs/gstreamer.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/gstreamer.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,268 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Multi-format decoder using Gstreamer. +""" +import queue +import tempfile +from threading import Event, Thread + +from ..exceptions import MediaDecodeException +from .base import StreamingSource, AudioData, AudioFormat, StaticSource +from . import MediaEncoder, MediaDecoder + +import gi +gi.require_version('Gst', '1.0') +from gi.repository import Gst, GLib + + +class GStreamerDecodeException(MediaDecodeException): + pass + + +class GLibMainLoopThread(Thread): + """A background Thread for a GLib MainLoop""" + def __init__(self): + super().__init__(daemon=True) + self.mainloop = GLib.MainLoop.new(None, False) + self.start() + + def run(self): + self.mainloop.run() + + +class GStreamerSource(StreamingSource): + + _sentinal = object() + + def __init__(self, filename, file=None): + self._pipeline = Gst.Pipeline() + + if file: + file.seek(0) + self._file = tempfile.NamedTemporaryFile(buffering=False) + self._file.write(file.read()) + filename = self._file.name + + # Create the major parts of the pipeline: + self.filesrc = Gst.ElementFactory.make("filesrc", None) + self.decoder = Gst.ElementFactory.make("decodebin", None) + self.converter = Gst.ElementFactory.make("audioconvert", None) + self.appsink = Gst.ElementFactory.make("appsink", None) + if not all((self.filesrc, self.decoder, self.converter, self.appsink)): + raise GStreamerDecodeException("Could not initialize GStreamer.") + + # Set callbacks for EOS and error messages: + self._pipeline.bus.add_signal_watch() + self._pipeline.bus.connect("message", self._message) + + # Set the file path to load: + self.filesrc.set_property("location", filename) + + # Set decoder callback handlers: + self.decoder.connect("pad-added", self._pad_added) + self.decoder.connect("no-more-pads", self._no_more_pads) + self.decoder.connect("unknown-type", self._unknown_type) + + # Set the sink's capabilities and behavior: + self.appsink.set_property('caps', Gst.Caps.from_string('audio/x-raw,format=S16LE,layout=interleaved')) + self.appsink.set_property('drop', False) + self.appsink.set_property('sync', False) + self.appsink.set_property('max-buffers', 0) # unlimited + self.appsink.set_property('emit-signals', True) + # The callback to receive decoded data: + self.appsink.connect("new-sample", self._new_sample) + + # Add all components to the pipeline: + self._pipeline.add(self.filesrc) + self._pipeline.add(self.decoder) + self._pipeline.add(self.converter) + self._pipeline.add(self.appsink) + # Link together necessary components: + self.filesrc.link(self.decoder) + self.decoder.link(self.converter) + self.converter.link(self.appsink) + + # Callback to notify once the sink is ready: + self.caps_handler = self.appsink.get_static_pad("sink").connect("notify::caps", self._notify_caps) + + # Set by callbacks: + self._pads = False + self._caps = False + self._pipeline.set_state(Gst.State.PLAYING) + self._queue = queue.Queue(5) + self._finished = Event() + # Wait until the is_ready event is set by a callback: + self._is_ready = Event() + if not self._is_ready.wait(timeout=1): + raise GStreamerDecodeException('Initialization Error') + + def __del__(self): + if hasattr(self, '_file'): + self._file.close() + + try: + self._pipeline.bus.remove_signal_watch() + self.filesrc.set_property("location", None) + self.appsink.get_static_pad("sink").disconnect(self.caps_handler) + while not self._queue.empty(): + self._queue.get_nowait() + self._pipeline.set_state(Gst.State.NULL) + except (ImportError, AttributeError): + pass + + def _notify_caps(self, pad, *args): + """notify::caps callback""" + self._caps = True + info = pad.get_current_caps().get_structure(0) + + self._duration = pad.get_peer().query_duration(Gst.Format.TIME).duration / Gst.SECOND + channels = info.get_int('channels')[1] + sample_rate = info.get_int('rate')[1] + sample_size = int("".join(filter(str.isdigit, info.get_string('format')))) + + self.audio_format = AudioFormat(channels=channels, sample_size=sample_size, sample_rate=sample_rate) + + # Allow __init__ to complete: + self._is_ready.set() + + def _pad_added(self, element, pad): + """pad-added callback""" + name = pad.query_caps(None).to_string() + if name.startswith('audio/x-raw'): + nextpad = self.converter.get_static_pad('sink') + if not nextpad.is_linked(): + self._pads = True + pad.link(nextpad) + + def _no_more_pads(self, element): + """Finished Adding pads""" + if not self._pads: + raise GStreamerDecodeException('No Streams Found') + + def _new_sample(self, sink): + """new-sample callback""" + # Pull the sample, and get it's buffer: + buffer = sink.emit('pull-sample').get_buffer() + # Extract a copy of the memory in the buffer: + mem = buffer.extract_dup(0, buffer.get_size()) + self._queue.put(mem) + return Gst.FlowReturn.OK + + @staticmethod + def _unknown_type(uridecodebin, decodebin, caps): + """unknown-type callback for unreadable files""" + streaminfo = caps.to_string() + if not streaminfo.startswith('audio/'): + return + raise GStreamerDecodeException(streaminfo) + + def _message(self, bus, message): + """The main message callback""" + if message.type == Gst.MessageType.EOS: + + self._queue.put(self._sentinal) + if not self._caps: + raise GStreamerDecodeException("Appears to be an unsupported file") + + elif message.type == Gst.MessageType.ERROR: + raise GStreamerDecodeException(message.parse_error()) + + def get_audio_data(self, num_bytes, compensation_time=0.0): + if self._finished.is_set(): + return None + + data = bytes() + while len(data) < num_bytes: + packet = self._queue.get() + if packet == self._sentinal: + self._finished.set() + break + data += packet + + if not data: + return None + + timestamp = self._pipeline.query_position(Gst.Format.TIME).cur / Gst.SECOND + duration = self.audio_format.bytes_per_second / len(data) + + return AudioData(data, len(data), timestamp, duration, []) + + def seek(self, timestamp): + # First clear any data in the queue: + while not self._queue.empty(): + self._queue.get_nowait() + + self._pipeline.seek_simple(Gst.Format.TIME, + Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT, + timestamp * Gst.SECOND) + self._finished.clear() + + +######################################### +# Decoder class: +######################################### + +class GStreamerDecoder(MediaDecoder): + + def __init__(self): + Gst.init(None) + self._glib_loop = GLibMainLoopThread() + + def get_file_extensions(self): + return '.mp3', '.flac', '.ogg', '.m4a' + + def decode(self, file, filename, streaming=True): + + if not any(filename.endswith(ext) for ext in self.get_file_extensions()): + # Refuse to decode anything not specifically listed in the supported + # file extensions list. This decoder does not yet support video, but + # it would still decode it and return only the Audio track. This is + # not desired, since the other decoders will not get a turn. Instead + # we bail out and let pyglet pass it to the next codec (FFmpeg). + raise GStreamerDecodeException('Unsupported format.') + + if streaming: + return GStreamerSource(filename, file) + else: + return StaticSource(GStreamerSource(filename, file)) + + +def get_decoders(): + return [GStreamerDecoder()] + + +def get_encoders(): + return [] diff -Nru pyglet-1.4.10/pyglet/media/codecs/__init__.py pyglet-1.5.14/pyglet/media/codecs/__init__.py --- pyglet-1.4.10/pyglet/media/codecs/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,26 +32,24 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- + +from pyglet.util import Codecs, Decoder, Encoder from .base import * -import os.path import pyglet _debug = pyglet.options['debug_media'] -_decoders = [] # List of registered MediaDecoders -_encoders = [] # List of registered MediaEncoders -_decoder_extensions = {} # Map str -> list of matching MediaDecoders -_encoder_extensions = {} # Map str -> list of matching MediaEncoders +_codecs = Codecs() +add_decoders = _codecs.add_decoders +get_decoders = _codecs.get_decoders +add_encoders = _codecs.add_encoders +get_encoders = _codecs.get_encoders -class MediaDecoder(object): - def get_file_extensions(self): - """Return a list or tuple of accepted file extensions, e.g. ['.wav', '.ogg'] - Lower-case only. - """ - return [] + +class MediaDecoder(Decoder): def decode(self, file, filename, streaming): """Read the given file object and return an instance of `Source` @@ -61,24 +59,10 @@ """ raise NotImplementedError() - def __hash__(self): - return hash(self.__class__.__name__) - - def __eq__(self, other): - return self.__class__.__name__ == other.__class__.__name__ - - def __repr__(self): - return "{0}{1}".format(self.__class__.__name__, self.get_file_extensions()) +class MediaEncoder(Encoder): -class MediaEncoder(object): - def get_file_extensions(self): - """Return a list or tuple of accepted file extensions, e.g. ['.wav', '.ogg'] - Lower-case only. - """ - return [] - - def encode(self, media, file, filename): + def encode(self, source, file, filename): """Encode the given source to the given file. `filename` provides a hint to the file format desired. options are encoder-specific, and unknown options should be ignored or @@ -86,69 +70,6 @@ """ raise NotImplementedError() - def __hash__(self): - return hash(self.__class__.__name__) - - def __eq__(self, other): - return self.__class__.__name__ == other.__class__.__name__ - - def __repr__(self): - return "{0}{1}".format(self.__class__.__name__, self.get_file_extensions()) - - -def get_decoders(filename=None): - """Get an ordered list of all decoders. If a `filename` is provided, - decoders supporting that extension will be ordered first in the list. - """ - decoders = [] - if filename: - extension = os.path.splitext(filename)[1].lower() - decoders += _decoder_extensions.get(extension, []) - decoders += [e for e in _decoders if e not in decoders] - return decoders - - -def get_encoders(filename=None): - """Get an ordered list of all encoders. If a `filename` is provided, - encoders supporting that extension will be ordered first in the list. - """ - encoders = [] - if filename: - extension = os.path.splitext(filename)[1].lower() - encoders += _encoder_extensions.get(extension, []) - encoders += [e for e in _encoders if e not in encoders] - return encoders - - -def add_decoders(module): - """Add a decoder module. The module must define `get_decoders`. Once - added, the appropriate decoders defined in the codec will be returned by - pyglet.media.codecs.get_decoders. - """ - for decoder in module.get_decoders(): - if decoder in _decoders: - continue - _decoders.append(decoder) - for extension in decoder.get_file_extensions(): - if extension not in _decoder_extensions: - _decoder_extensions[extension] = [] - _decoder_extensions[extension].append(decoder) - - -def add_encoders(module): - """Add an encoder module. The module must define `get_encoders`. Once - added, the appropriate encoders defined in the codec will be returned by - pyglet.media.codecs.get_encoders. - """ - for encoder in module.get_encoders(): - if encoder in _encoders: - continue - _encoders.append(encoder) - for extension in encoder.get_file_extensions(): - if extension not in _encoder_extensions: - _encoder_extensions[extension] = [] - _encoder_extensions[extension].append(encoder) - def add_default_media_codecs(): # Add all bundled codecs. These should be listed in order of @@ -157,6 +78,23 @@ try: from . import wave add_decoders(wave) + add_encoders(wave) + except ImportError: + pass + + if pyglet.compat_platform.startswith('linux'): + try: + from . import gstreamer + add_decoders(gstreamer) + except ImportError: + pass + + try: + if pyglet.compat_platform in ('win32', 'cygwin'): + from pyglet.libs.win32.constants import WINDOWS_VISTA_OR_GREATER + if WINDOWS_VISTA_OR_GREATER: # Supports Vista and above. + from . import wmf + add_decoders(wmf) except ImportError: pass diff -Nru pyglet-1.4.10/pyglet/media/codecs/wave.py pyglet-1.5.14/pyglet/media/codecs/wave.py --- pyglet-1.4.10/pyglet/media/codecs/wave.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/wave.py 2020-12-31 21:03:32.000000000 +0000 @@ -35,14 +35,13 @@ """Decoder for RIFF Wave files, using the standard library wave module. """ -from __future__ import absolute_import, division -from ..exceptions import MediaDecodeException +import wave + +from ..exceptions import MediaDecodeException, MediaEncodeException from .base import StreamingSource, AudioData, AudioFormat, StaticSource from . import MediaEncoder, MediaDecoder -import wave - class WAVEDecodeException(MediaDecodeException): pass @@ -60,13 +59,9 @@ except wave.Error as e: raise WAVEDecodeException(e) - # PYTHON2: use the named tuple instead of explicit unpacking - # parameters = self._wave.getparams() nchannels, sampwidth, framerate, nframes, comptype, compname = self._wave.getparams() - self.audio_format = AudioFormat(channels=nchannels, - sample_size=sampwidth * 8, - sample_rate=framerate) + self.audio_format = AudioFormat(channels=nchannels, sample_size=sampwidth * 8, sample_rate=framerate) self._bytes_per_frame = nchannels * sampwidth self._duration = nframes / framerate @@ -76,7 +71,8 @@ self._wave.rewind() def __del__(self): - self._file.close() + if hasattr(self, '_file'): + self._file.close() def get_audio_data(self, num_bytes, compensation_time=0.0): num_frames = max(1, num_bytes // self._bytes_per_frame) @@ -111,9 +107,44 @@ return StaticSource(WaveSource(filename, file)) +class WaveEncoder(MediaEncoder): + + def get_file_extensions(self): + return '.wav', '.wave', '.riff' + + def encode(self, source, file, filename): + """Save the Source to disk as a standard RIFF Wave. + + A standard RIFF wave header will be added to the raw PCM + audio data when it is saved to disk. + + :Parameters: + `filename` : str + The file name to save as. + + """ + + extension = filename.split('.')[-1].lower() + if f".{extension}" not in self.get_file_extensions(): + raise MediaDecodeException("Invalid Format") + + source.seek(0) + wave_writer = wave.open(file, mode='wb') + wave_writer.setnchannels(source.audio_format.channels) + wave_writer.setsampwidth(source.audio_format.sample_size // 8) + wave_writer.setframerate(source.audio_format.sample_rate) + + # Save the data in 1-second chunks: + chunksize = source.audio_format.bytes_per_second + audiodata = source.get_audio_data(chunksize) + while audiodata: + wave_writer.writeframes(audiodata.data) + audiodata = source.get_audio_data(chunksize) + + def get_decoders(): return [WaveDecoder()] def get_encoders(): - return [] + return [WaveEncoder()] diff -Nru pyglet-1.4.10/pyglet/media/codecs/wmf.py pyglet-1.5.14/pyglet/media/codecs/wmf.py --- pyglet-1.4.10/pyglet/media/codecs/wmf.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/codecs/wmf.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,896 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- +import os +import platform +import warnings + +from pyglet import com, image +from pyglet.util import debug_print +from pyglet.libs.win32 import _kernel32 as kernel32 +from pyglet.libs.win32 import _ole32 as ole32 +from pyglet.libs.win32.constants import * +from pyglet.libs.win32.types import * +from pyglet.media import Source, MediaDecodeException +from pyglet.media.codecs import AudioFormat, AudioData, VideoFormat, MediaDecoder, StaticSource + +_debug = debug_print('debug_media') + +try: + mfreadwrite = 'mfreadwrite' + mfplat = 'mfplat' + + # System32 and SysWOW64 folders are opposite perception in Windows x64. + # System32 = x64 dll's | SysWOW64 = x86 dlls + # By default ctypes only seems to look in system32 regardless of Python architecture, which has x64 dlls. + if platform.architecture()[0] == '32bit': + if platform.machine().endswith('64'): # Machine is 64 bit, Python is 32 bit. + mfreadwrite = os.path.join(os.environ['WINDIR'], 'SysWOW64', 'mfreadwrite.dll') + mfplat = os.path.join(os.environ['WINDIR'], 'SysWOW64', 'mfplat.dll') + + mfreadwrite_lib = ctypes.windll.LoadLibrary(mfreadwrite) + mfplat_lib = ctypes.windll.LoadLibrary(mfplat) +except OSError: + # Doesn't exist? Should stop import of library. + raise ImportError('Could not load WMF library.') + +MF_SOURCE_READERF_ERROR = 0x00000001 +MF_SOURCE_READERF_ENDOFSTREAM = 0x00000002 +MF_SOURCE_READERF_NEWSTREAM = 0x00000004 +MF_SOURCE_READERF_NATIVEMEDIATYPECHANGED = 0x00000010 +MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED = 0x00000020 +MF_SOURCE_READERF_STREAMTICK = 0x00000100 + +# Audio attributes +MF_LOW_LATENCY = com.GUID(0x9c27891a, 0xed7a, 0x40e1, 0x88, 0xe8, 0xb2, 0x27, 0x27, 0xa0, 0x24, 0xee) + +# Audio information +MF_MT_ALL_SAMPLES_INDEPENDENT = com.GUID(0xc9173739, 0x5e56, 0x461c, 0xb7, 0x13, 0x46, 0xfb, 0x99, 0x5c, 0xb9, 0x5f) +MF_MT_FIXED_SIZE_SAMPLES = com.GUID(0xb8ebefaf, 0xb718, 0x4e04, 0xb0, 0xa9, 0x11, 0x67, 0x75, 0xe3, 0x32, 0x1b) +MF_MT_SAMPLE_SIZE = com.GUID(0xdad3ab78, 0x1990, 0x408b, 0xbc, 0xe2, 0xeb, 0xa6, 0x73, 0xda, 0xcc, 0x10) +MF_MT_COMPRESSED = com.GUID(0x3afd0cee, 0x18f2, 0x4ba5, 0xa1, 0x10, 0x8b, 0xea, 0x50, 0x2e, 0x1f, 0x92) +MF_MT_WRAPPED_TYPE = com.GUID(0x4d3f7b23, 0xd02f, 0x4e6c, 0x9b, 0xee, 0xe4, 0xbf, 0x2c, 0x6c, 0x69, 0x5d) +MF_MT_AUDIO_NUM_CHANNELS = com.GUID(0x37e48bf5, 0x645e, 0x4c5b, 0x89, 0xde, 0xad, 0xa9, 0xe2, 0x9b, 0x69, 0x6a) +MF_MT_AUDIO_SAMPLES_PER_SECOND = com.GUID(0x5faeeae7, 0x0290, 0x4c31, 0x9e, 0x8a, 0xc5, 0x34, 0xf6, 0x8d, 0x9d, 0xba) +MF_MT_AUDIO_FLOAT_SAMPLES_PER_SECOND = com.GUID(0xfb3b724a, 0xcfb5, 0x4319, 0xae, 0xfe, 0x6e, 0x42, 0xb2, 0x40, 0x61, 0x32) +MF_MT_AUDIO_AVG_BYTES_PER_SECOND = com.GUID(0x1aab75c8, 0xcfef, 0x451c, 0xab, 0x95, 0xac, 0x03, 0x4b, 0x8e, 0x17, 0x31) +MF_MT_AUDIO_BLOCK_ALIGNMENT = com.GUID(0x322de230, 0x9eeb, 0x43bd, 0xab, 0x7a, 0xff, 0x41, 0x22, 0x51, 0x54, 0x1d) +MF_MT_AUDIO_BITS_PER_SAMPLE = com.GUID(0xf2deb57f, 0x40fa, 0x4764, 0xaa, 0x33, 0xed, 0x4f, 0x2d, 0x1f, 0xf6, 0x69) +MF_MT_AUDIO_VALID_BITS_PER_SAMPLE = com.GUID(0xd9bf8d6a, 0x9530, 0x4b7c, 0x9d, 0xdf, 0xff, 0x6f, 0xd5, 0x8b, 0xbd, 0x06) +MF_MT_AUDIO_SAMPLES_PER_BLOCK = com.GUID(0xaab15aac, 0xe13a, 0x4995, 0x92, 0x22, 0x50, 0x1e, 0xa1, 0x5c, 0x68, 0x77) +MF_MT_AUDIO_CHANNEL_MASK = com.GUID(0x55fb5765, 0x644a, 0x4caf, 0x84, 0x79, 0x93, 0x89, 0x83, 0xbb, 0x15, 0x88) +MF_PD_DURATION = com.GUID(0x6c990d33, 0xbb8e, 0x477a, 0x85, 0x98, 0xd, 0x5d, 0x96, 0xfc, 0xd8, 0x8a) + + +# Media types categories +MF_MT_MAJOR_TYPE = com.GUID(0x48eba18e, 0xf8c9, 0x4687, 0xbf, 0x11, 0x0a, 0x74, 0xc9, 0xf9, 0x6a, 0x8f) +MF_MT_SUBTYPE = com.GUID(0xf7e34c9a, 0x42e8, 0x4714, 0xb7, 0x4b, 0xcb, 0x29, 0xd7, 0x2c, 0x35, 0xe5) + +# Major types +MFMediaType_Audio = com.GUID(0x73647561, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71) +MFMediaType_Video = com.GUID(0x73646976, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71) +MFMediaType_Protected = com.GUID(0x7b4b6fe6, 0x9d04, 0x4494, 0xbe, 0x14, 0x7e, 0x0b, 0xd0, 0x76, 0xc8, 0xe4) +MFMediaType_Image = com.GUID(0x72178C23, 0xE45B, 0x11D5, 0xBC, 0x2A, 0x00, 0xB0, 0xD0, 0xF3, 0xF4, 0xAB) +MFMediaType_HTML = com.GUID(0x72178C24, 0xE45B, 0x11D5, 0xBC, 0x2A, 0x00, 0xB0, 0xD0, 0xF3, 0xF4, 0xAB) +MFMediaType_Subtitle = com.GUID(0xa6d13581, 0xed50, 0x4e65, 0xae, 0x08, 0x26, 0x06, 0x55, 0x76, 0xaa, 0xcc) + +# Video subtypes, attributes, and enums (Uncompressed) +D3DFMT_X8R8G8B8 = 22 +D3DFMT_P8 = 41 +D3DFMT_A8R8G8B8 = 21 +MFVideoFormat_RGB32 = com.GUID(D3DFMT_X8R8G8B8, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71) +MFVideoFormat_RGB8 = com.GUID(D3DFMT_P8, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71) +MFVideoFormat_ARGB32 = com.GUID(D3DFMT_A8R8G8B8, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71) + +MFVideoInterlace_Progressive = 2 +MF_MT_INTERLACE_MODE = com.GUID(0xe2724bb8, 0xe676, 0x4806, 0xb4, 0xb2, 0xa8, 0xd6, 0xef, 0xb4, 0x4c, 0xcd) +MF_MT_FRAME_SIZE = com.GUID(0x1652c33d, 0xd6b2, 0x4012, 0xb8, 0x34, 0x72, 0x03, 0x08, 0x49, 0xa3, 0x7d) +MF_MT_FRAME_RATE = com.GUID(0xc459a2e8, 0x3d2c, 0x4e44, 0xb1, 0x32, 0xfe, 0xe5, 0x15, 0x6c, 0x7b, 0xb0) +MF_MT_PIXEL_ASPECT_RATIO = com.GUID(0xc6376a1e, 0x8d0a, 0x4027, 0xbe, 0x45, 0x6d, 0x9a, 0x0a, 0xd3, 0x9b, 0xb6) +MF_MT_DRM_FLAGS = com.GUID(0x8772f323, 0x355a, 0x4cc7, 0xbb, 0x78, 0x6d, 0x61, 0xa0, 0x48, 0xae, 0x82) +MF_MT_DEFAULT_STRIDE = com.GUID(0x644b4e48, 0x1e02, 0x4516, 0xb0, 0xeb, 0xc0, 0x1c, 0xa9, 0xd4, 0x9a, 0xc6) + +# Audio Subtypes (Uncompressed) +WAVE_FORMAT_PCM = 1 +WAVE_FORMAT_IEEE_FLOAT = 3 +MFAudioFormat_PCM = com.GUID(WAVE_FORMAT_PCM, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71) +MFAudioFormat_Float = com.GUID(WAVE_FORMAT_IEEE_FLOAT, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71) + +# Image subtypes. +MFImageFormat_RGB32 = com.GUID(0x00000016, 0x0000, 0x0010, 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71) +MFImageFormat_JPEG = com.GUID(0x19e4a5aa, 0x5662, 0x4fc5, 0xa0, 0xc0, 0x17, 0x58, 0x02, 0x8e, 0x10, 0x57) + +# Video attributes +# Enables hardware decoding +MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS = com.GUID(0xa634a91c, 0x822b, 0x41b9, 0xa4, 0x94, 0x4d, 0xe4, 0x64, 0x36, 0x12, + 0xb0) +# Enable video decoding +MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING = com.GUID(0xfb394f3d, 0xccf1, 0x42ee, 0xbb, 0xb3, 0xf9, 0xb8, 0x45, 0xd5, + 0x68, 0x1d) +MF_SOURCE_READER_D3D_MANAGER = com.GUID(0xec822da2, 0xe1e9, 0x4b29, 0xa0, 0xd8, 0x56, 0x3c, 0x71, 0x9f, 0x52, 0x69) +MF_MEDIA_ENGINE_DXGI_MANAGER = com.GUID(0x065702da, 0x1094, 0x486d, 0x86, 0x17, 0xee, 0x7c, 0xc4, 0xee, 0x46, 0x48) +MF_SOURCE_READER_ENABLE_ADVANCED_VIDEO_PROCESSING = com.GUID(0xf81da2c, 0xb537, 0x4672, 0xa8, 0xb2, 0xa6, 0x81, 0xb1, + 0x73, 0x7, 0xa3) + +# Some common errors +MF_E_INVALIDSTREAMNUMBER = -1072875853 # 0xC00D36B3 +MF_E_UNSUPPORTED_BYTESTREAM_TYPE = -1072875836 # 0xC00D36C4 +MF_E_NO_MORE_TYPES = 0xC00D36B9 +MF_E_TOPO_CODEC_NOT_FOUND = -1072868846 # 0xC00D5212 + + +VT_I8 = 20 # Only enum we care about: https://docs.microsoft.com/en-us/windows/win32/api/wtypes/ne-wtypes-varenum + + +def timestamp_from_wmf(timestamp): # 100-nanoseconds + return float(timestamp) / 10000000 + + +def timestamp_to_wmf(timestamp): # 100-nanoseconds + return int(timestamp * 10000000) + + +class IMFAttributes(com.pIUnknown): + _methods_ = [ + ('GetItem', + com.STDMETHOD()), + ('GetItemType', + com.STDMETHOD()), + ('CompareItem', + com.STDMETHOD()), + ('Compare', + com.STDMETHOD()), + ('GetUINT32', + com.STDMETHOD(com.REFIID, POINTER(c_uint32))), + ('GetUINT64', + com.STDMETHOD(com.REFIID, POINTER(c_uint64))), + ('GetDouble', + com.STDMETHOD()), + ('GetGUID', + com.STDMETHOD(com.REFIID, POINTER(com.GUID))), + ('GetStringLength', + com.STDMETHOD()), + ('GetString', + com.STDMETHOD()), + ('GetAllocatedString', + com.STDMETHOD()), + ('GetBlobSize', + com.STDMETHOD()), + ('GetBlob', + com.STDMETHOD()), + ('GetAllocatedBlob', + com.STDMETHOD()), + ('GetUnknown', + com.STDMETHOD()), + ('SetItem', + com.STDMETHOD()), + ('DeleteItem', + com.STDMETHOD()), + ('DeleteAllItems', + com.STDMETHOD()), + ('SetUINT32', + com.STDMETHOD(com.REFIID, c_uint32)), + ('SetUINT64', + com.STDMETHOD()), + ('SetDouble', + com.STDMETHOD()), + ('SetGUID', + com.STDMETHOD(com.REFIID, com.REFIID)), + ('SetString', + com.STDMETHOD()), + ('SetBlob', + com.STDMETHOD()), + ('SetUnknown', + com.STDMETHOD(com.REFIID, com.pIUnknown)), + ('LockStore', + com.STDMETHOD()), + ('UnlockStore', + com.STDMETHOD()), + ('GetCount', + com.STDMETHOD()), + ('GetItemByIndex', + com.STDMETHOD()), + ('CopyAllItems', + com.STDMETHOD(c_void_p)), # IMFAttributes + ] + + +class IMFMediaBuffer(com.pIUnknown): + _methods_ = [ + ('Lock', + com.STDMETHOD(POINTER(POINTER(BYTE)), POINTER(DWORD), POINTER(DWORD))), + ('Unlock', + com.STDMETHOD()), + ('GetCurrentLength', + com.STDMETHOD(POINTER(DWORD))), + ('SetCurrentLength', + com.STDMETHOD(DWORD)), + ('GetMaxLength', + com.STDMETHOD(POINTER(DWORD))) + ] + + +class IMFSample(IMFAttributes, com.pIUnknown): + _methods_ = [ + ('GetSampleFlags', + com.STDMETHOD()), + ('SetSampleFlags', + com.STDMETHOD()), + ('GetSampleTime', + com.STDMETHOD()), + ('SetSampleTime', + com.STDMETHOD()), + ('GetSampleDuration', + com.STDMETHOD(POINTER(c_ulonglong))), + ('SetSampleDuration', + com.STDMETHOD(DWORD, IMFMediaBuffer)), + ('GetBufferCount', + com.STDMETHOD(POINTER(DWORD))), + ('GetBufferByIndex', + com.STDMETHOD(DWORD, IMFMediaBuffer)), + ('ConvertToContiguousBuffer', + com.STDMETHOD(POINTER(IMFMediaBuffer))), # out + ('AddBuffer', + com.STDMETHOD(POINTER(DWORD))), + ('RemoveBufferByIndex', + com.STDMETHOD()), + ('RemoveAllBuffers', + com.STDMETHOD()), + ('GetTotalLength', + com.STDMETHOD(POINTER(DWORD))), + ('CopyToBuffer', + com.STDMETHOD()), + ] + + +class IMFMediaType(IMFAttributes, com.pIUnknown): + _methods_ = [ + ('GetMajorType', + com.STDMETHOD()), + ('IsCompressedFormat', + com.STDMETHOD()), + ('IsEqual', + com.STDMETHOD()), + ('GetRepresentation', + com.STDMETHOD()), + ('FreeRepresentation', + com.STDMETHOD()), + ] + + +class IMFByteStream(com.pIUnknown): + _methods_ = [ + ('GetCapabilities', + com.STDMETHOD()), + ('GetLength', + com.STDMETHOD()), + ('SetLength', + com.STDMETHOD()), + ('GetCurrentPosition', + com.STDMETHOD()), + ('SetCurrentPosition', + com.STDMETHOD(c_ulonglong)), + ('IsEndOfStream', + com.STDMETHOD()), + ('Read', + com.STDMETHOD()), + ('BeginRead', + com.STDMETHOD()), + ('EndRead', + com.STDMETHOD()), + ('Write', + com.STDMETHOD(POINTER(BYTE), ULONG, POINTER(ULONG))), + ('BeginWrite', + com.STDMETHOD()), + ('EndWrite', + com.STDMETHOD()), + ('Seek', + com.STDMETHOD()), + ('Flush', + com.STDMETHOD()), + ('Close', + com.STDMETHOD()), + ] + + +class IMFSourceReader(com.pIUnknown): + _methods_ = [ + ('GetStreamSelection', + com.STDMETHOD(DWORD, POINTER(BOOL))), # in, out + ('SetStreamSelection', + com.STDMETHOD(DWORD, BOOL)), + ('GetNativeMediaType', + com.STDMETHOD(DWORD, DWORD, POINTER(IMFMediaType))), + ('GetCurrentMediaType', + com.STDMETHOD(DWORD, POINTER(IMFMediaType))), + ('SetCurrentMediaType', + com.STDMETHOD(DWORD, POINTER(DWORD), IMFMediaType)), + ('SetCurrentPosition', + com.STDMETHOD(com.REFIID, POINTER(PROPVARIANT))), + ('ReadSample', + com.STDMETHOD(DWORD, DWORD, POINTER(DWORD), POINTER(DWORD), POINTER(c_longlong), POINTER(IMFSample))), + ('Flush', + com.STDMETHOD(DWORD)), # in + ('GetServiceForStream', + com.STDMETHOD()), + ('GetPresentationAttribute', + com.STDMETHOD(DWORD, com.REFIID, POINTER(PROPVARIANT))), + ] + + +class WAVEFORMATEX(ctypes.Structure): + _fields_ = [ + ('wFormatTag', WORD), + ('nChannels', WORD), + ('nSamplesPerSec', DWORD), + ('nAvgBytesPerSec', DWORD), + ('nBlockAlign', WORD), + ('wBitsPerSample', WORD), + ('cbSize', WORD), + ] + + def __repr__(self): + return 'WAVEFORMATEX(wFormatTag={}, nChannels={}, nSamplesPerSec={}, nAvgBytesPersec={}' \ + ', nBlockAlign={}, wBitsPerSample={}, cbSize={})'.format( + self.wFormatTag, self.nChannels, self.nSamplesPerSec, + self.nAvgBytesPerSec, self.nBlockAlign, self.wBitsPerSample, + self.cbSize) + + +# Stream constants +MF_SOURCE_READER_ALL_STREAMS = 0xfffffffe +MF_SOURCE_READER_ANY_STREAM = 4294967294 # 0xfffffffe +MF_SOURCE_READER_FIRST_AUDIO_STREAM = 4294967293 # 0xfffffffd +MF_SOURCE_READER_FIRST_VIDEO_STREAM = 0xfffffffc +MF_SOURCE_READER_MEDIASOURCE = 0xffffffff + +# Version calculation +if WINDOWS_7_OR_GREATER: + MF_SDK_VERSION = 0x0002 +else: + MF_SDK_VERSION = 0x0001 + +MF_API_VERSION = 0x0070 # Only used in Vista. + +MF_VERSION = (MF_SDK_VERSION << 16 | MF_API_VERSION) + +MFStartup = mfplat_lib.MFStartup +MFStartup.restype = HRESULT +MFStartup.argtypes = [LONG, DWORD] + +MFShutdown = mfplat_lib.MFShutdown +MFShutdown.restype = HRESULT +MFShutdown.argtypes = [] + +MFCreateAttributes = mfplat_lib.MFCreateAttributes +MFCreateAttributes.restype = HRESULT +MFCreateAttributes.argtypes = [POINTER(IMFAttributes), c_uint32] # attributes, cInitialSize + +MFCreateSourceReaderFromURL = mfreadwrite_lib.MFCreateSourceReaderFromURL +MFCreateSourceReaderFromURL.restype = HRESULT +MFCreateSourceReaderFromURL.argtypes = [LPCWSTR, IMFAttributes, POINTER(IMFSourceReader)] + +MFCreateSourceReaderFromByteStream = mfreadwrite_lib.MFCreateSourceReaderFromByteStream +MFCreateSourceReaderFromByteStream.restype = HRESULT +MFCreateSourceReaderFromByteStream.argtypes = [IMFByteStream, IMFAttributes, POINTER(IMFSourceReader)] + +if WINDOWS_7_OR_GREATER: + MFCreateMFByteStreamOnStream = mfplat_lib.MFCreateMFByteStreamOnStream + MFCreateMFByteStreamOnStream.restype = HRESULT + MFCreateMFByteStreamOnStream.argtypes = [c_void_p, POINTER(IMFByteStream)] + +MFCreateTempFile = mfplat_lib.MFCreateTempFile +MFCreateTempFile.restype = HRESULT +MFCreateTempFile.argtypes = [UINT, UINT, UINT, POINTER(IMFByteStream)] + +MFCreateMediaType = mfplat_lib.MFCreateMediaType +MFCreateMediaType.restype = HRESULT +MFCreateMediaType.argtypes = [POINTER(IMFMediaType)] + +MFCreateWaveFormatExFromMFMediaType = mfplat_lib.MFCreateWaveFormatExFromMFMediaType +MFCreateWaveFormatExFromMFMediaType.restype = HRESULT +MFCreateWaveFormatExFromMFMediaType.argtypes = [IMFMediaType, POINTER(POINTER(WAVEFORMATEX)), POINTER(c_uint32), c_uint32] + + +class WMFSource(Source): + low_latency = True # Quicker latency but possible quality loss. + + decode_audio = True + decode_video = True + + def __init__(self, filename, file=None): + assert any([self.decode_audio, self.decode_video]), "Source must decode audio, video, or both, not none." + self._current_audio_sample = None + self._current_audio_buffer = None + self._current_video_sample = None + self._current_video_buffer = None + self._timestamp = 0 + self._attributes = None + self._stream_obj = None + self._imf_bytestream = None + self._wfx = None + self._stride = None + + self.set_config_attributes() + + # Create SourceReader + self._source_reader = IMFSourceReader() + + # If it's a file, we need to load it as a stream. + if file is not None: + data = file.read() + + self._imf_bytestream = IMFByteStream() + + data_len = len(data) + + if WINDOWS_7_OR_GREATER: + # Stole code from GDIPlus for older IStream support. + hglob = kernel32.GlobalAlloc(GMEM_MOVEABLE, data_len) + ptr = kernel32.GlobalLock(hglob) + ctypes.memmove(ptr, data, data_len) + kernel32.GlobalUnlock(hglob) + + # Create IStream + self._stream_obj = com.pIUnknown() + ole32.CreateStreamOnHGlobal(hglob, True, ctypes.byref(self._stream_obj)) + + # MFCreateMFByteStreamOnStreamEx for future async operations exists, however Windows 8+ only. Requires new interface + # (Also unsure how/if new Windows async functions and callbacks work with ctypes.) + MFCreateMFByteStreamOnStream(self._stream_obj, ctypes.byref(self._imf_bytestream)) + else: + # Vista does not support MFCreateMFByteStreamOnStream. + # HACK: Create file in Windows temp folder to write our byte data to. + # (Will be automatically deleted when IMFByteStream is Released.) + MFCreateTempFile(MF_ACCESSMODE_READWRITE, + MF_OPENMODE_DELETE_IF_EXIST, + MF_FILEFLAGS_NONE, + ctypes.byref(self._imf_bytestream)) + + wrote_length = ULONG() + data_ptr = cast(data, POINTER(BYTE)) + self._imf_bytestream.Write(data_ptr, data_len, ctypes.byref(wrote_length)) + self._imf_bytestream.SetCurrentPosition(0) + + if wrote_length.value != data_len: + raise MediaDecodeException("Could not write all of the data to the bytestream file.") + + try: + MFCreateSourceReaderFromByteStream(self._imf_bytestream, self._attributes, ctypes.byref(self._source_reader)) + except OSError as err: + raise MediaDecodeException(err) from None + else: + # We can just load from filename if no file object specified.. + try: + MFCreateSourceReaderFromURL(filename, self._attributes, ctypes.byref(self._source_reader)) + except OSError as err: + raise MediaDecodeException(err) from None + + if self.decode_audio: + self._load_audio() + + if self.decode_video: + self._load_video() + + assert self.audio_format or self.video_format, "Source was decoded, but no video or audio streams were found." + + # Get duration of the media file after everything has been ok to decode. + try: + prop = PROPVARIANT() + self._source_reader.GetPresentationAttribute(MF_SOURCE_READER_MEDIASOURCE, + ctypes.byref(MF_PD_DURATION), + ctypes.byref(prop)) + + self._duration = timestamp_from_wmf(prop.llVal) + ole32.PropVariantClear(ctypes.byref(prop)) + except OSError: + warnings.warn("Could not determine duration of media file: '{}'.".format(filename)) + + def _load_audio(self, stream=MF_SOURCE_READER_FIRST_AUDIO_STREAM): + """ Prepares the audio stream for playback by detecting if it's compressed and attempting to decompress to PCM. + Default: Only get the first available audio stream. + """ + # Will be an audio file. + self._audio_stream_index = stream + + # Get what the native/real media type is (audio only) + imfmedia = IMFMediaType() + + try: + self._source_reader.GetNativeMediaType(self._audio_stream_index, 0, ctypes.byref(imfmedia)) + except OSError as err: + if err.winerror == MF_E_INVALIDSTREAMNUMBER: + assert _debug('WMFAudioDecoder: No audio stream found.') + return + + # Get Major media type (Audio, Video, etc) + # TODO: Make GUID take no arguments for a null version: + guid_audio_type = com.GUID(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + + imfmedia.GetGUID(MF_MT_MAJOR_TYPE, ctypes.byref(guid_audio_type)) + + if guid_audio_type == MFMediaType_Audio: + assert _debug('WMFAudioDecoder: Found Audio Stream.') + + # Deselect any other streams if we don't need them. (Small speedup) + if not self.decode_video: + self._source_reader.SetStreamSelection(MF_SOURCE_READER_ANY_STREAM, False) + + # Select first audio stream. + self._source_reader.SetStreamSelection(MF_SOURCE_READER_FIRST_AUDIO_STREAM, True) + + # Check sub media type, AKA what kind of codec + guid_compressed = com.GUID(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + imfmedia.GetGUID(MF_MT_SUBTYPE, ctypes.byref(guid_compressed)) + + if guid_compressed == MFAudioFormat_PCM or guid_compressed == MFAudioFormat_Float: + assert _debug('WMFAudioDecoder: Found Uncompressed Audio:', guid_compressed) + else: + assert _debug('WMFAudioDecoder: Found Compressed Audio:', guid_compressed) + # If audio is compressed, attempt to decompress it by forcing source reader to use PCM + mf_mediatype = IMFMediaType() + + MFCreateMediaType(ctypes.byref(mf_mediatype)) + mf_mediatype.SetGUID(MF_MT_MAJOR_TYPE, MFMediaType_Audio) + mf_mediatype.SetGUID(MF_MT_SUBTYPE, MFAudioFormat_PCM) + + try: + self._source_reader.SetCurrentMediaType(self._audio_stream_index, None, mf_mediatype) + except OSError as err: # Can't decode codec. + raise MediaDecodeException(err) from None + + # Current media type should now be properly decoded at this point. + decoded_media_type = IMFMediaType() # Maybe reusing older IMFMediaType will work? + self._source_reader.GetCurrentMediaType(self._audio_stream_index, ctypes.byref(decoded_media_type)) + + wfx_length = ctypes.c_uint32() + wfx = POINTER(WAVEFORMATEX)() + + MFCreateWaveFormatExFromMFMediaType(decoded_media_type, + ctypes.byref(wfx), + ctypes.byref(wfx_length), + 0) + + self._wfx = wfx.contents + self.audio_format = AudioFormat(channels=self._wfx.nChannels, + sample_size=self._wfx.wBitsPerSample, + sample_rate=self._wfx.nSamplesPerSec) + else: + assert _debug('WMFAudioDecoder: Audio stream not found') + + def get_format(self): + """Returns the WAVEFORMATEX data which has more information thah audio_format""" + return self._wfx + + def _load_video(self, stream=MF_SOURCE_READER_FIRST_VIDEO_STREAM): + self._video_stream_index = stream + + # Get what the native/real media type is (video only) + imfmedia = IMFMediaType() + + try: + self._source_reader.GetCurrentMediaType(self._video_stream_index, ctypes.byref(imfmedia)) + except OSError as err: + if err.winerror == MF_E_INVALIDSTREAMNUMBER: + assert _debug('WMFVideoDecoder: No video stream found.') + return + + assert _debug('WMFVideoDecoder: Found Video Stream') + + # All video is basically compressed, try to decompress. + uncompressed_mt = IMFMediaType() + MFCreateMediaType(ctypes.byref(uncompressed_mt)) + + imfmedia.CopyAllItems(uncompressed_mt) + + imfmedia.Release() + + uncompressed_mt.SetGUID(MF_MT_SUBTYPE, MFVideoFormat_RGB32) + uncompressed_mt.SetUINT32(MF_MT_INTERLACE_MODE, MFVideoInterlace_Progressive) + uncompressed_mt.SetUINT32(MF_MT_ALL_SAMPLES_INDEPENDENT, 1) + + try: + self._source_reader.SetCurrentMediaType(self._video_stream_index, None, uncompressed_mt) + except OSError as err: # Can't decode codec. + raise MediaDecodeException(err) from None + + height, width = self._get_attribute_size(uncompressed_mt, MF_MT_FRAME_SIZE) + + self.video_format = VideoFormat(width=width, height=height) + assert _debug('WMFVideoDecoder: Frame width: {} height: {}'.format(width, height)) + + # Frame rate + den, num = self._get_attribute_size(uncompressed_mt, MF_MT_FRAME_RATE) + self.video_format.frame_rate = num / den + assert _debug('WMFVideoDecoder: Frame Rate: {} / {} = {}'.format(num, den, self.video_format.frame_rate)) + + # Sometimes it can return negative? Variable bit rate? Needs further tests and examples. + if self.video_format.frame_rate < 0: + self.video_format.frame_rate = 30000 / 1001 + assert _debug('WARNING: Negative frame rate, attempting to use default, but may experience issues.') + + # Pixel ratio + den, num = self._get_attribute_size(uncompressed_mt, MF_MT_PIXEL_ASPECT_RATIO) + self.video_format.sample_aspect = num / den + assert _debug('WMFVideoDecoder: Pixel Ratio: {} / {} = {}'.format(num, den, self.video_format.sample_aspect)) + + def get_audio_data(self, num_bytes, compensation_time=0.0): + flags = DWORD() + timestamp = ctypes.c_longlong() + audio_data_length = DWORD() + + # If we have an audio sample already in use and we call this again, release the memory of buffer and sample. + # Can only release after the data is played or else glitches and pops can be heard. + if self._current_audio_sample: + self._current_audio_buffer.Release() + self._current_audio_sample.Release() + + self._current_audio_sample = IMFSample() + self._current_audio_buffer = IMFMediaBuffer() + + while True: + self._source_reader.ReadSample(self._audio_stream_index, 0, None, ctypes.byref(flags), + ctypes.byref(timestamp), ctypes.byref(self._current_audio_sample)) + + if flags.value & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED: + assert _debug('WMFAudioDecoder: Data is no longer valid.') + break + + if flags.value & MF_SOURCE_READERF_ENDOFSTREAM: + assert _debug('WMFAudioDecoder: End of data from stream source.') + break + + if not self._current_audio_sample: + assert _debug('WMFAudioDecoder: No sample.') + continue + + # Convert to single buffer as a sample could potentially(rarely) have multiple buffers. + self._current_audio_sample.ConvertToContiguousBuffer(ctypes.byref(self._current_audio_buffer)) + + audio_data_ptr = POINTER(BYTE)() + + self._current_audio_buffer.Lock(ctypes.byref(audio_data_ptr), None, ctypes.byref(audio_data_length)) + self._current_audio_buffer.Unlock() + + audio_data = create_string_buffer(audio_data_length.value) + memmove(audio_data, audio_data_ptr, audio_data_length.value) + + return AudioData(audio_data, + audio_data_length.value, + timestamp_from_wmf(timestamp.value), + audio_data_length.value / self.audio_format.sample_rate, + []) + + return None + + def get_next_video_frame(self, skip_empty_frame=True): + video_data_length = DWORD() + flags = DWORD() + timestamp = ctypes.c_longlong() + + if self._current_video_sample: + self._current_video_buffer.Release() + self._current_video_sample.Release() + + self._current_video_sample = IMFSample() + self._current_video_buffer = IMFMediaBuffer() + + while True: + self._source_reader.ReadSample(self._video_stream_index, 0, None, ctypes.byref(flags), + ctypes.byref(timestamp), ctypes.byref(self._current_video_sample)) + + if flags.value & MF_SOURCE_READERF_CURRENTMEDIATYPECHANGED: + assert _debug('WMFVideoDecoder: Data is no longer valid.') + + # Get Major media type (Audio, Video, etc) + new = IMFMediaType() + self._source_reader.GetCurrentMediaType(self._video_stream_index, ctypes.byref(new)) + + # Sometimes this happens once. I think this only + # changes if the stride is added/changed before playback? + stride = ctypes.c_uint32() + new.GetUINT32(MF_MT_DEFAULT_STRIDE, ctypes.byref(stride)) + + self._stride = stride.value + + if flags.value & MF_SOURCE_READERF_ENDOFSTREAM: + self._timestamp = None + assert _debug('WMFVideoDecoder: End of data from stream source.') + break + + if not self._current_video_sample: + assert _debug('WMFVideoDecoder: No sample.') + continue + + self._current_video_buffer = IMFMediaBuffer() + + # Convert to single buffer as a sample could potentially have multiple buffers. + self._current_video_sample.ConvertToContiguousBuffer(ctypes.byref(self._current_video_buffer)) + + video_data = POINTER(BYTE)() + + self._current_video_buffer.Lock(ctypes.byref(video_data), None, ctypes.byref(video_data_length)) + + width = self.video_format.width + height = self.video_format.height + + # buffer = ctypes.create_string_buffer(size) + self._timestamp = timestamp_from_wmf(timestamp.value) + + self._current_video_buffer.Unlock() + + # This is made with the assumption that the video frame will be blitted into the player texture immediately + # after, and then cleared next frame attempt. + return image.ImageData(width, height, 'BGRA', video_data, self._stride) + + return None + + def get_next_video_timestamp(self): + return self._timestamp + + def seek(self, timestamp): + timestamp = min(timestamp, self._duration) if self._duration else timestamp + + prop = PROPVARIANT() + prop.vt = VT_I8 + prop.llVal = timestamp_to_wmf(timestamp) + + pos_com = com.GUID(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + try: + self._source_reader.SetCurrentPosition(pos_com, prop) + except OSError as err: + warnings.warn(str(err)) + + ole32.PropVariantClear(ctypes.byref(prop)) + + @staticmethod + def _get_attribute_size(attributes, guidKey): + """ Convert int64 attributes to int32""" # HI32/LOW32 + + size = ctypes.c_uint64() + attributes.GetUINT64(guidKey, size) + lParam = size.value + + x = ctypes.c_int32(lParam).value + y = ctypes.c_int32(lParam >> 32).value + return x, y + + def set_config_attributes(self): + """ Here we set user specified attributes, by default we try to set low latency mode. (Win7+)""" + if self.low_latency or self.decode_video: + self._attributes = IMFAttributes() + + MFCreateAttributes(ctypes.byref(self._attributes), 3) + + if self.low_latency and WINDOWS_7_OR_GREATER: + self._attributes.SetUINT32(ctypes.byref(MF_LOW_LATENCY), 1) + + assert _debug('WMFAudioDecoder: Setting configuration attributes.') + + # If it's a video we need to enable the streams to be accessed. + if self.decode_video: + self._attributes.SetUINT32(ctypes.byref(MF_READWRITE_ENABLE_HARDWARE_TRANSFORMS), 1) + self._attributes.SetUINT32(ctypes.byref(MF_SOURCE_READER_ENABLE_VIDEO_PROCESSING), 1) + + assert _debug('WMFVideoDecoder: Setting configuration attributes.') + + def __del__(self): + if self._stream_obj: + self._stream_obj.Release() + + if self._imf_bytestream: + self._imf_bytestream.Release() + + if self._current_audio_sample: + self._current_audio_buffer.Release() + self._current_audio_sample.Release() + + if self._current_video_sample: + self._current_video_buffer.Release() + self._current_video_sample.Release() + + +######################################### +# Decoder class: +######################################### + +class WMFDecoder(MediaDecoder): + def __init__(self): + + self.ole32 = None + self.MFShutdown = None + + try: + # Coinitialize supposed to be called for COMs? + ole32.CoInitializeEx(None, COINIT_MULTITHREADED) + except OSError as err: + warnings.warn(str(err)) + + try: + MFStartup(MF_VERSION, 0) + except OSError as err: + raise ImportError('WMF could not startup:', err.strerror) + + self.extensions = self._build_decoder_extensions() + + self.ole32 = ole32 + self.MFShutdown = MFShutdown + + assert _debug('Windows Media Foundation: Initialized.') + + @staticmethod + def _build_decoder_extensions(): + """Extension support varies depending on OS version.""" + extensions = [] + if WINDOWS_VISTA_OR_GREATER: + extensions.extend(['.asf', '.wma', '.wmv', + '.mp3', + '.sami', '.smi', + ]) + + if WINDOWS_7_OR_GREATER: + extensions.extend(['.3g2', '.3gp', '.3gp2', '.3gp', + '.aac', '.adts', + '.avi', + '.m4a', '.m4v', '.mov', '.mp4', + # '.wav' # Can do wav, but we have a WAVE decoder. + ]) + + if WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER: + extensions.extend(['.mkv', '.flac', '.ogg']) + + return extensions + + def get_file_extensions(self): + return self.extensions + + def decode(self, file, filename, streaming=True): + if streaming: + return WMFSource(filename, file) + else: + return StaticSource(WMFSource(filename, file)) + + def __del__(self): + if self.MFShutdown is not None: + self.MFShutdown() + if self.ole32 is not None: + self.ole32.CoUninitialize() + + +def get_decoders(): + return [WMFDecoder()] + + +def get_encoders(): + return [] diff -Nru pyglet-1.4.10/pyglet/media/devices/base.py pyglet-1.5.14/pyglet/media/devices/base.py --- pyglet-1.4.10/pyglet/media/devices/base.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/devices/base.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,89 @@ +from abc import ABCMeta, abstractmethod + +from pyglet import event +from pyglet.util import with_metaclass + + +class DeviceState: + ACTIVE = "active" + DISABLED = "disabled" + MISSING = "missing" + UNPLUGGED = "unplugged" + + +class DeviceFlow: + OUTPUT = "output" + INPUT = "input" + INPUT_OUTPUT = "input/output" + + +class AudioDevice: + """Base class for a platform independent audio device. + _platform_state and _platform_flow is used to make device state numbers.""" + _platform_state = {} # Must be defined by the parent. + _platform_flow = {} # Must be defined by the parent. + + def __init__(self, dev_id, name, description, flow, state): + self.id = dev_id + self.flow = flow + self.state = state + self.name = name + self.description = description + + def __repr__(self): + return "{}(name={}, state={}, flow={})".format( + self.__class__.__name__, self.name, self._platform_state[self.state], self._platform_flow[self.flow]) + + +class AbstractAudioDeviceManager(with_metaclass(ABCMeta, event.EventDispatcher, object)): + + def __del__(self): + """Required to remove handlers before exit, as it can cause problems with the event system's weakrefs.""" + self.remove_handlers(self) + + @abstractmethod + def get_default_output(self): + """Returns a default active output device or None if none available.""" + pass + + @abstractmethod + def get_default_input(self): + """Returns a default active input device or None if none available.""" + pass + + @abstractmethod + def get_output_devices(self): + """Returns a list of all active output devices.""" + pass + + @abstractmethod + def get_input_devices(self): + """Returns a list of all active input devices.""" + pass + + @abstractmethod + def get_all_devices(self): + """Returns a list of all audio devices, no matter what state they are in.""" + pass + + def on_device_state_changed(self, device, old_state, new_state): + """Event, occurs when the state of a device changes, provides old state and new state.""" + pass + + def on_device_added(self, device): + """Event, occurs when a new device is added to the system.""" + pass + + def on_device_removed(self, device): + """Event, occurs when an existing device is removed from the system.""" + pass + + def on_default_changed(self, device): + """Event, occurs when the default audio device changes.""" + pass + + +AbstractAudioDeviceManager.register_event_type('on_device_state_changed') +AbstractAudioDeviceManager.register_event_type('on_device_added') +AbstractAudioDeviceManager.register_event_type('on_device_removed') +AbstractAudioDeviceManager.register_event_type('on_default_changed') diff -Nru pyglet-1.4.10/pyglet/media/devices/__init__.py pyglet-1.5.14/pyglet/media/devices/__init__.py --- pyglet-1.4.10/pyglet/media/devices/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/devices/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,31 @@ +import atexit + +import pyglet + + +def get_audio_device_manager(): + global _audio_device_manager + + if _audio_device_manager: + return _audio_device_manager + + _audio_device_manager = None + + if pyglet.compat_platform == 'win32': + from pyglet.media.devices.win32 import Win32AudioDeviceManager + _audio_device_manager = Win32AudioDeviceManager() + + return _audio_device_manager + + +def _delete_manager(): + """Deletes existing manager. If audio device manager is stored anywhere. + Required to remove handlers before exit, as it can cause problems with the event system's weakrefs.""" + global _audio_device_manager + _audio_device_manager = None + + +global _audio_device_manager +_audio_device_manager = None + +atexit.register(_delete_manager) diff -Nru pyglet-1.4.10/pyglet/media/devices/win32.py pyglet-1.5.14/pyglet/media/devices/win32.py --- pyglet-1.4.10/pyglet/media/devices/win32.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/devices/win32.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,306 @@ +import pyglet +from pyglet import com +from pyglet.libs.win32 import _ole32 as ole32 +from pyglet.libs.win32.constants import CLSCTX_INPROC_SERVER +from pyglet.libs.win32.types import * +from pyglet.media.devices import base +from pyglet.util import debug_print + +_debug = debug_print('debug_media') + +EDataFlow = UINT +# Audio rendering stream. Audio data flows from the application to the audio endpoint device, which renders the stream. +eRender = 0 + +# Audio capture stream. Audio data flows from the audio endpoint device that captures the stream, to the application. +eCapture = 1 + +# Audio rendering or capture stream. Audio data can flow either from the application to the audio endpoint device, +# or from the audio endpoint device to the application. +eAll = 2 + +EDataFlow_enum_count = 3 + +ERole = UINT +eConsole = 0 # Games, system notification sounds, and voice commands. +eMultimedia = 1 # Music, movies, narration, and live music recording. +eCommunications = 2 # Voice communications (talking to another person). +ERole_enum_count = 3 + +DEVICE_STATE_ACTIVE = 0x00000001 +DEVICE_STATE_DISABLED = 0x00000002 +DEVICE_STATE_NOTPRESENT = 0x00000004 +DEVICE_STATE_UNPLUGGED = 0x00000008 +DEVICE_STATEMASK_ALL = 0x0000000F + +STGM_READ = 0 +STGM_WRITE = 1 +STGM_READWRITE = 2 + +VT_LPWSTR = 0x001F + + +class PROPERTYKEY(ctypes.Structure): + _fields_ = [ + ('fmtid', com.GUID), + ('pid', DWORD), + ] + + def __repr__(self): + return "PROPERTYKEY({}, pid={})".format(self.fmtid, self.pid) + + +REFPROPERTYKEY = PROPERTYKEY + +PKEY_Device_FriendlyName = PROPERTYKEY( + com.GUID(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 14) +PKEY_Device_DeviceDesc = PROPERTYKEY( + com.GUID(0xa45c254e, 0xdf1c, 0x4efd, 0x80, 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0), 2) +PKEY_DeviceInterface_FriendlyName = PROPERTYKEY( + com.GUID(0x026e516e, 0xb814, 0x414b, 0x83, 0xcd, 0x85, 0x6d, 0x6f, 0xef, 0x48, 0x22), 2) + + +class IPropertyStore(com.pIUnknown): + _methods_ = [ + ('GetCount', + com.STDMETHOD(POINTER(DWORD))), + ('GetAt', + com.STDMETHOD(DWORD, POINTER(PROPERTYKEY))), + ('GetValue', + com.STDMETHOD(REFPROPERTYKEY, POINTER(PROPVARIANT))), + ('SetValue', + com.STDMETHOD()), + ('Commit', + com.STDMETHOD()), + ] + + +CLSID_MMDeviceEnumerator = com.GUID(0xbcde0395, 0xe52f, 0x467c, 0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e) +IID_IMMDeviceEnumerator = com.GUID(0xa95664d2, 0x9614, 0x4f35, 0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6) + + +class IMMNotificationClient(com.IUnknown): + _methods_ = [ + ('OnDeviceStateChanged', + com.METHOD(ctypes.c_void_p, ctypes.c_void_p, LPCWSTR, DWORD)), + ('OnDeviceAdded', + com.METHOD(ctypes.c_void_p, ctypes.c_void_p, LPCWSTR)), + ('OnDeviceRemoved', + com.METHOD(ctypes.c_void_p, ctypes.c_void_p, LPCWSTR)), + ('OnDefaultDeviceChanged', + com.METHOD(ctypes.c_void_p, ctypes.c_void_p, EDataFlow, ERole, LPCWSTR)), + ('OnPropertyValueChanged', + com.METHOD(ctypes.c_void_p, ctypes.c_void_p, LPCWSTR, PROPERTYKEY)), + ] + + +class AudioNotificationCB(com.COMObject): + _interfaces_ = [IMMNotificationClient] + + def __init__(self, audio_devices): + super().__init__() + self.audio_devices = audio_devices + self._lost = False + + def OnDeviceStateChanged(self, this, pwstrDeviceId, dwNewState): + device = self.audio_devices.get_cached_device(pwstrDeviceId) + + old_state = device.state + assert _debug( + "Audio device {} changed state. From state: {} to state: {}".format(device.name, old_state, dwNewState)) + + device.state = dwNewState + self.audio_devices.dispatch_event('on_device_state_changed', device, old_state, dwNewState) + + def OnDeviceAdded(self, this, pwstrDeviceId): + assert _debug("Audio device was added {}".format(pwstrDeviceId)) + self.audio_devices.dispatch_event('on_device_added', pwstrDeviceId) + + def OnDeviceRemoved(self, this, pwstrDeviceId): + assert _debug("Audio device was removed {}".format(pwstrDeviceId)) + self.audio_devices.dispatch_event('on_device_removed', pwstrDeviceId) + + def OnDefaultDeviceChanged(self, this, flow, role, pwstrDeviceId): + # Only support eConsole role right now + if role == 0: + if pwstrDeviceId is None: + device = None + else: + device = self.audio_devices.get_cached_device(pwstrDeviceId) + + self.audio_devices.dispatch_event('on_default_changed', device) + + def OnPropertyValueChanged(self, this, pwstrDeviceId, key): + pass + + +class IMMDevice(com.pIUnknown): + _methods_ = [ + ('Activate', + com.STDMETHOD(com.REFIID, DWORD, POINTER(PROPVARIANT))), + ('OpenPropertyStore', + com.STDMETHOD(UINT, POINTER(IPropertyStore))), + ('GetId', + com.STDMETHOD(POINTER(LPWSTR))), + ('GetState', + com.STDMETHOD(POINTER(DWORD))), + ] + + +class IMMDeviceCollection(com.pIUnknown): + _methods_ = [ + ('GetCount', + com.STDMETHOD(POINTER(UINT))), + ('Item', + com.STDMETHOD(UINT, POINTER(IMMDevice))), + ] + + +class IMMDeviceEnumerator(com.pIUnknown): + _methods_ = [ + ('EnumAudioEndpoints', + com.STDMETHOD(EDataFlow, DWORD, c_void_p)), + ('GetDefaultAudioEndpoint', + com.STDMETHOD(EDataFlow, ERole, ctypes.POINTER(IMMDevice))), + ('GetDevice', + com.STDMETHOD(LPCWSTR, POINTER(IMMDevice))), + ('RegisterEndpointNotificationCallback', + com.STDMETHOD(POINTER(IMMNotificationClient))), + ('UnregisterEndpointNotificationCallback', + com.STDMETHOD()), + ] + + +class Win32AudioDevice(base.AudioDevice): + _platform_state = { + DEVICE_STATE_ACTIVE: base.DeviceState.ACTIVE, + DEVICE_STATE_DISABLED: base.DeviceState.DISABLED, + DEVICE_STATE_NOTPRESENT: base.DeviceState.MISSING, + DEVICE_STATE_UNPLUGGED: base.DeviceState.UNPLUGGED + } + + _platform_flow = { + eRender: base.DeviceFlow.OUTPUT, + eCapture: base.DeviceFlow.INPUT, + eAll: base.DeviceFlow.INPUT_OUTPUT + } + + +class Win32AudioDeviceManager(base.AbstractAudioDeviceManager): + def __init__(self): + self._device_enum = IMMDeviceEnumerator() + ole32.CoCreateInstance(CLSID_MMDeviceEnumerator, None, CLSCTX_INPROC_SERVER, IID_IMMDeviceEnumerator, + byref(self._device_enum)) + + # Keep all devices cached, and the callback can keep them updated. + self.devices = self._query_all_devices() + + super().__init__() + + self._callback = AudioNotificationCB(self) + self._device_enum.RegisterEndpointNotificationCallback(self._callback) + + def get_default_output(self): + """Attempts to retrieve a default audio output for the system. Returns None if no available devices found.""" + try: + device = IMMDevice() + self._device_enum.GetDefaultAudioEndpoint(eRender, eConsole, byref(device)) + dev_id, name, desc, dev_state = self.get_device_info(device) + device.Release() + + pid = self.get_cached_device(dev_id) + pid.state = dev_state + return pid + except OSError: + assert _debug("No default audio output was found.") + return None + + def get_default_input(self): + """Attempts to retrieve a default audio input for the system. Returns None if no available devices found.""" + try: + device = IMMDevice() + self._device_enum.GetDefaultAudioEndpoint(eCapture, eConsole, byref(device)) + dev_id, name, desc, dev_state = self.get_device_info(device) + device.Release() + + pid = self.get_cached_device(dev_id) + pid.state = dev_state + return pid + except OSError: + assert _debug("No default input output was found.") + return None + + def get_cached_device(self, dev_id): + """Gets the cached devices, so we can reduce calls to COM and tell current state vs new states.""" + for device in self.devices: + if device.id == dev_id: + return device + + raise Exception("Attempted to get a device that does not exist.") + + # return None + + def get_output_devices(self, state=DEVICE_STATE_ACTIVE): + return [device for device in self.devices if device.state == state and device.flow == eRender] + + def get_input_devices(self, state=DEVICE_STATE_ACTIVE): + return [device for device in self.devices if device.state == state and device.flow == eCapture] + + def get_all_devices(self): + return self.devices + + def _query_all_devices(self): + return self.get_devices(flow=eRender, state=DEVICE_STATEMASK_ALL) + self.get_devices(flow=eCapture, + state=DEVICE_STATEMASK_ALL) + + def get_device_info(self, device): + """Return the ID, Name, and Description of the Audio Device.""" + store = IPropertyStore() + device.OpenPropertyStore(STGM_READ, byref(store)) + + dev_id = LPWSTR() + device.GetId(byref(dev_id)) + + name = self.get_pkey_value(store, PKEY_Device_FriendlyName) + description = self.get_pkey_value(store, PKEY_Device_DeviceDesc) + + state = DWORD() + device.GetState(byref(state)) + + store.Release() + + return dev_id.value, name, description, state.value + + def get_devices(self, flow=eRender, state=DEVICE_STATE_ACTIVE): + """Get's all of the specified devices (by default, all output and active).""" + collection = IMMDeviceCollection() + self._device_enum.EnumAudioEndpoints(flow, state, byref(collection)) + + count = UINT() + collection.GetCount(byref(count)) + + devices = [] + for i in range(count.value): + dev_itf = IMMDevice() + collection.Item(i, byref(dev_itf)) + + dev_id, name, desc, dev_state = self.get_device_info(dev_itf) + device = Win32AudioDevice(dev_id, name, desc, flow, dev_state) + dev_itf.Release() + devices.append(device) + + collection.Release() + + return devices + + @staticmethod + def get_pkey_value(store, pkey): + try: + propvar = PROPVARIANT() + store.GetValue(pkey, byref(propvar)) + value = propvar.pwszVal + ole32.PropVariantClear(propvar) + except Exception as err: + value = "Unknown" + + return value diff -Nru pyglet-1.4.10/pyglet/media/drivers/base.py pyglet-1.5.14/pyglet/media/drivers/base.py --- pyglet-1.4.10/pyglet/media/drivers/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/base.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,13 +32,14 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from future.utils import with_metaclass import math import weakref from abc import ABCMeta, abstractmethod +from pyglet.util import with_metaclass + class AbstractAudioPlayer(with_metaclass(ABCMeta, object)): """Base class for driver audio players. @@ -69,7 +70,15 @@ self.audio_diff_avg_count = 0 self.audio_diff_cum = 0.0 self.audio_diff_avg_coef = math.exp(math.log10(0.01) / self.AUDIO_DIFF_AVG_NB) - self.audio_diff_threshold = 0.1 # Experimental. ffplay computes it differently + self.audio_diff_threshold = 0.1 # Experimental. ffplay computes it differently + + def on_driver_destroy(self): + """Called before the audio driver is going to be destroyed (a planned destroy).""" + pass + + def on_driver_reset(self): + """Called after the audio driver has been re-initialized.""" + pass @abstractmethod def play(self): @@ -190,7 +199,7 @@ @property def source(self): - "Source to play from." + """Source to play from.""" return self._source @source.setter diff -Nru pyglet-1.4.10/pyglet/media/drivers/directsound/adaptation.py pyglet-1.5.14/pyglet/media/drivers/directsound/adaptation.py --- pyglet-1.4.10/pyglet/media/drivers/directsound/adaptation.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/directsound/adaptation.py 2020-12-04 14:53:12.000000000 +0000 @@ -32,14 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import, print_function import ctypes import math import pyglet from . import interface -from pyglet.debug import debug_print +from pyglet.util import debug_print from pyglet.media.events import MediaEvent from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer from pyglet.media.drivers.listener import AbstractListener @@ -130,7 +129,6 @@ self.refill(self._buffer_size) def __del__(self): - assert _debug("Delete DirectSoundAudioPlayer") # We decrease the IDirectSound refcount self.driver._ds_driver._native_dsound.Release() @@ -139,7 +137,7 @@ def play(self): assert _debug('DirectSound play') - pyglet.clock.schedule_interval_soft(self._check_refill, 0.1) + pyglet.clock.schedule_interval(self._check_refill, 0.1) if not self._playing: self._get_audiodata() # prebuffer if needed @@ -404,7 +402,6 @@ return DirectSoundListener(self._ds_listener, self._ds_driver.primary_buffer) def delete(self): - assert _debug("Delete DirectSoundDriver") # Make sure the _ds_listener is deleted before the _ds_driver self._ds_listener = None @@ -414,9 +411,6 @@ self._ds_listener = ds_listener self._ds_buffer = ds_buffer - def __del__(self): - assert _debug("Delete DirectSoundListener") - def _set_volume(self, volume): self._volume = volume self._ds_buffer.volume = _gain2db(volume) diff -Nru pyglet-1.4.10/pyglet/media/drivers/directsound/exceptions.py pyglet-1.5.14/pyglet/media/drivers/directsound/exceptions.py --- pyglet-1.4.10/pyglet/media/drivers/directsound/exceptions.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/directsound/exceptions.py 2020-11-10 10:36:26.000000000 +0000 @@ -46,6 +46,3 @@ def __repr__(self): return "{}: Error {}".format(self.__class__.__name__, self.hresult) - - - diff -Nru pyglet-1.4.10/pyglet/media/drivers/directsound/__init__.py pyglet-1.5.14/pyglet/media/drivers/directsound/__init__.py --- pyglet-1.4.10/pyglet/media/drivers/directsound/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/directsound/__init__.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import from . import adaptation from .exceptions import DirectSoundException, DirectSoundNativeError @@ -41,4 +40,5 @@ def create_audio_driver(): return adaptation.DirectSoundDriver() + __all__ = ["create_audio_driver", "DirectSoundException", "DirectSoundNativeError"] diff -Nru pyglet-1.4.10/pyglet/media/drivers/directsound/interface.py pyglet-1.5.14/pyglet/media/drivers/directsound/interface.py --- pyglet-1.4.10/pyglet/media/drivers/directsound/interface.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/directsound/interface.py 2020-11-16 14:49:52.000000000 +0000 @@ -35,11 +35,11 @@ """ Pythonic interface to DirectSound. """ -from collections import namedtuple import ctypes import weakref +from collections import namedtuple -from pyglet.debug import debug_print +from pyglet.util import debug_print from pyglet.window.win32 import _user32 from . import lib_dsound as lib @@ -47,12 +47,13 @@ _debug = debug_print('debug_media') + def _check(hresult): if hresult != lib.DS_OK: raise DirectSoundNativeError(hresult) -class DirectSoundDriver(object): +class DirectSoundDriver: def __init__(self): assert _debug('Constructing DirectSoundDriver') @@ -73,9 +74,11 @@ self.primary_buffer = self._buffer_factory.create_primary_buffer() def __del__(self): - assert _debug("Delete interface.DirectSoundDriver") - self.primary_buffer = None - self._native_dsound.Release() + try: + self.primary_buffer = None + self._native_dsound.Release() + except ValueError: + pass def create_buffer(self, audio_format): return self._buffer_factory.create_buffer(audio_format) @@ -84,7 +87,7 @@ return self.primary_buffer.create_listener() -class DirectSoundBufferFactory(object): +class DirectSoundBufferFactory: default_buffer_size = 2.0 def __init__(self, native_dsound): @@ -92,9 +95,6 @@ # interface.DirectSoundDriver self._native_dsound = weakref.proxy(native_dsound) - def __del__(self): - assert _debug("Delete interface.DirectSoundBufferFactory") - def create_buffer(self, audio_format): buffer_size = int(audio_format.sample_rate * self.default_buffer_size) wave_format = self._create_wave_format(audio_format) @@ -154,7 +154,8 @@ return buffer_desc -class DirectSoundBuffer(object): + +class DirectSoundBuffer: def __init__(self, native_buffer, audio_format, buffer_size): self.audio_format = audio_format self.buffer_size = buffer_size @@ -169,10 +170,12 @@ self._native_buffer3d = None def __del__(self): - self.delete() + try: + self.delete() + except OSError: + pass def delete(self): - assert _debug("Delete interface.DirectSoundBuffer from AudioFormat {}".format(self.audio_format)) if self._native_buffer is not None: self._native_buffer.Stop() self._native_buffer.Release() @@ -384,7 +387,7 @@ self._native_buffer.Stop() ) - class _WritePointer(object): + class _WritePointer: def __init__(self): self.audio_ptr_1 = ctypes.c_void_p() self.audio_length_1 = lib.DWORD() @@ -414,7 +417,7 @@ ) -class DirectSoundListener(object): +class DirectSoundListener: def __init__(self, ds_buffer, native_listener): # We only keep a weakref to ds_buffer as it is owned by # interface.DirectSound or a DirectSoundAudioPlayer @@ -425,7 +428,6 @@ self.delete() def delete(self): - assert _debug("Delete interface.DirectSoundListener") if self._native_listener: self._native_listener.Release() self._native_listener = None @@ -436,7 +438,7 @@ _check( self._native_listener.GetPosition(ctypes.byref(vector)) ) - return (vector.x, vector.y, vector.z) + return vector.x, vector.y, vector.z @position.setter def position(self, value): @@ -451,12 +453,10 @@ _check( self._native_listener.GetOrientation(ctypes.byref(front), ctypes.byref(top)) ) - return (front.x, front.y, front.z, top.x, top.y, top.z) + return front.x, front.y, front.z, top.x, top.y, top.z @orientation.setter def orientation(self, orientation): _check( self._native_listener.SetOrientation(*(list(orientation) + [lib.DS3D_IMMEDIATE])) ) - - diff -Nru pyglet-1.4.10/pyglet/media/drivers/directsound/lib_dsound.py pyglet-1.5.14/pyglet/media/drivers/directsound/lib_dsound.py --- pyglet-1.4.10/pyglet/media/drivers/directsound/lib_dsound.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/directsound/lib_dsound.py 2020-12-31 21:03:32.000000000 +0000 @@ -159,7 +159,7 @@ ] LPDS3DLISTENER = ctypes.POINTER(DS3DLISTENER) -class IDirectSoundBuffer(com.IUnknown): +class IDirectSoundBuffer(com.pIUnknown): _methods_ = [ ('GetCaps', com.STDMETHOD(LPDSBCAPS)), @@ -205,7 +205,7 @@ IID_IDirectSound3DListener = com.GUID( 0x279AFA84, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60) -class IDirectSound3DListener(com.IUnknown): +class IDirectSound3DListener(com.pIUnknown): _methods_ = [ ('GetAllParameters', com.STDMETHOD(LPDS3DLISTENER)), @@ -243,7 +243,7 @@ IID_IDirectSound3DBuffer = com.GUID( 0x279AFA86, 0x4981, 0x11CE, 0xA5, 0x21, 0x00, 0x20, 0xAF, 0x0B, 0xE5, 0x60) -class IDirectSound3DBuffer(com.IUnknown): +class IDirectSound3DBuffer(com.pIUnknown): _methods_ = [ ('GetAllParameters', com.STDMETHOD(LPDS3DBUFFER)), @@ -283,7 +283,7 @@ com.STDMETHOD(D3DVALUE, D3DVALUE, D3DVALUE, DWORD)), ] -class IDirectSound(com.IUnknown): +class IDirectSound(com.pIUnknown): _methods_ = [ ('CreateSoundBuffer', com.STDMETHOD(LPDSBUFFERDESC, diff -Nru pyglet-1.4.10/pyglet/media/drivers/__init__.py pyglet-1.5.14/pyglet/media/drivers/__init__.py --- pyglet-1.4.10/pyglet/media/drivers/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -33,8 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- """Drivers for playing back media.""" -from __future__ import print_function -from __future__ import absolute_import import atexit @@ -69,22 +67,34 @@ from . import pulse _audio_driver = pulse.create_audio_driver() break - elif driver_name == 'openal': - from . import openal - _audio_driver = openal.create_audio_driver() - break + elif driver_name == 'xaudio2': + from pyglet.libs.win32.constants import WINDOWS_8_OR_GREATER + if WINDOWS_8_OR_GREATER: + from . import xaudio2 + try: + _audio_driver = xaudio2.create_audio_driver() + except ImportError: + # Occurs when default audio device is not found, and cannot bind. + pass + break elif driver_name == 'directsound': from . import directsound _audio_driver = directsound.create_audio_driver() break + elif driver_name == 'openal': + from . import openal + _audio_driver = openal.create_audio_driver() + break elif driver_name == 'silent': - _audio_driver = None + from . import silent + _audio_driver = silent.create_audio_driver() break except Exception: if _debug: print('Error importing driver %s:' % driver_name) import traceback traceback.print_exc() + return _audio_driver diff -Nru pyglet-1.4.10/pyglet/media/drivers/listener.py pyglet-1.5.14/pyglet/media/drivers/listener.py --- pyglet-1.4.10/pyglet/media/drivers/listener.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/listener.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -from builtins import object # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -34,7 +33,8 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- from abc import ABCMeta, abstractmethod -from future.utils import with_metaclass + +from pyglet.util import with_metaclass class AbstractListener(with_metaclass(ABCMeta, object)): diff -Nru pyglet-1.4.10/pyglet/media/drivers/openal/adaptation.py pyglet-1.5.14/pyglet/media/drivers/openal/adaptation.py --- pyglet-1.4.10/pyglet/media/drivers/openal/adaptation.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/openal/adaptation.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,14 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import print_function -from __future__ import absolute_import import weakref import pyglet from . import interface -from pyglet.debug import debug_print +from pyglet.util import debug_print from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer from pyglet.media.events import MediaEvent from pyglet.media.drivers.listener import AbstractListener @@ -165,7 +163,6 @@ self.refill(self.ideal_buffer_size) def __del__(self): - assert _debug("Delete OpenALAudioPlayer") self.delete() def delete(self): @@ -192,10 +189,8 @@ def stop(self): assert _debug('OpenALAudioPlayer.stop()') pyglet.clock.unschedule(self._check_refill) - assert self.driver is not None assert self.alsource is not None - self.alsource.pause() self._playing = False diff -Nru pyglet-1.4.10/pyglet/media/drivers/openal/__init__.py pyglet-1.5.14/pyglet/media/drivers/openal/__init__.py --- pyglet-1.4.10/pyglet/media/drivers/openal/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/openal/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,9 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id$ -from __future__ import print_function -from __future__ import absolute_import from .adaptation import OpenALDriver diff -Nru pyglet-1.4.10/pyglet/media/drivers/openal/interface.py pyglet-1.5.14/pyglet/media/drivers/openal/interface.py --- pyglet-1.4.10/pyglet/media/drivers/openal/interface.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/openal/interface.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,19 +32,15 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id$ -from __future__ import print_function -from __future__ import absolute_import -from builtins import str import ctypes -from collections import namedtuple import weakref +from collections import namedtuple from . import lib_openal as al from . import lib_alc as alc import pyglet -from pyglet.debug import debug_print +from pyglet.util import debug_print from pyglet.media.exceptions import MediaException _debug = debug_print('debug_media') @@ -65,7 +61,7 @@ self.message) -class OpenALObject(object): +class OpenALObject: """Base class for OpenAL objects.""" @classmethod def _check_error(cls, message=None): diff -Nru pyglet-1.4.10/pyglet/media/drivers/openal/lib_alc.py pyglet-1.5.14/pyglet/media/drivers/openal/lib_alc.py --- pyglet-1.4.10/pyglet/media/drivers/openal/lib_alc.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/openal/lib_alc.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,25 +32,22 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for openal +"""Wrapper for openal Generated with: ../tools/wraptypes/wrap.py /usr/include/AL/alc.h -lopenal -olib_alc.py .. Hacked to fix ALCvoid argtypes. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" import ctypes from ctypes import * -import sys import pyglet.lib -_lib = pyglet.lib.load_library('openal', win32='openal32', - framework='/System/Library/Frameworks/OpenAL.framework') +_lib = pyglet.lib.load_library('openal', + win32='openal32', + framework='OpenAL') _int_types = (c_int16, c_int32) if hasattr(ctypes, 'c_int64'): @@ -62,6 +59,7 @@ if sizeof(t) == sizeof(c_size_t): c_ptrdiff_t = t + class c_void(Structure): # c_void_p is a buggy return type, converting to int, so # POINTER(None) == c_void_p is actually written as @@ -69,77 +67,90 @@ _fields_ = [('dummy', c_int)] +ALC_API = 0 # /usr/include/AL/alc.h:19 +ALCAPI = 0 # /usr/include/AL/alc.h:37 +ALC_INVALID = 0 # /usr/include/AL/alc.h:39 +ALC_VERSION_0_1 = 1 # /usr/include/AL/alc.h:42 + -ALC_API = 0 # /usr/include/AL/alc.h:19 -ALCAPI = 0 # /usr/include/AL/alc.h:37 -ALC_INVALID = 0 # /usr/include/AL/alc.h:39 -ALC_VERSION_0_1 = 1 # /usr/include/AL/alc.h:42 class struct_ALCdevice_struct(Structure): __slots__ = [ ] + + struct_ALCdevice_struct._fields_ = [ ('_opaque_struct', c_int) ] + class struct_ALCdevice_struct(Structure): __slots__ = [ ] + + struct_ALCdevice_struct._fields_ = [ ('_opaque_struct', c_int) ] -ALCdevice = struct_ALCdevice_struct # /usr/include/AL/alc.h:44 +ALCdevice = struct_ALCdevice_struct # /usr/include/AL/alc.h:44 + + class struct_ALCcontext_struct(Structure): __slots__ = [ ] + + struct_ALCcontext_struct._fields_ = [ ('_opaque_struct', c_int) ] + class struct_ALCcontext_struct(Structure): __slots__ = [ ] + + struct_ALCcontext_struct._fields_ = [ ('_opaque_struct', c_int) ] -ALCcontext = struct_ALCcontext_struct # /usr/include/AL/alc.h:45 -ALCboolean = c_char # /usr/include/AL/alc.h:49 -ALCchar = c_char # /usr/include/AL/alc.h:52 -ALCbyte = c_char # /usr/include/AL/alc.h:55 -ALCubyte = c_ubyte # /usr/include/AL/alc.h:58 -ALCshort = c_short # /usr/include/AL/alc.h:61 -ALCushort = c_ushort # /usr/include/AL/alc.h:64 -ALCint = c_int # /usr/include/AL/alc.h:67 -ALCuint = c_uint # /usr/include/AL/alc.h:70 -ALCsizei = c_int # /usr/include/AL/alc.h:73 -ALCenum = c_int # /usr/include/AL/alc.h:76 -ALCfloat = c_float # /usr/include/AL/alc.h:79 -ALCdouble = c_double # /usr/include/AL/alc.h:82 -ALCvoid = None # /usr/include/AL/alc.h:85 -ALC_FALSE = 0 # /usr/include/AL/alc.h:91 -ALC_TRUE = 1 # /usr/include/AL/alc.h:94 -ALC_FREQUENCY = 4103 # /usr/include/AL/alc.h:99 -ALC_REFRESH = 4104 # /usr/include/AL/alc.h:104 -ALC_SYNC = 4105 # /usr/include/AL/alc.h:109 -ALC_MONO_SOURCES = 4112 # /usr/include/AL/alc.h:114 -ALC_STEREO_SOURCES = 4113 # /usr/include/AL/alc.h:119 -ALC_NO_ERROR = 0 # /usr/include/AL/alc.h:128 -ALC_INVALID_DEVICE = 40961 # /usr/include/AL/alc.h:133 -ALC_INVALID_CONTEXT = 40962 # /usr/include/AL/alc.h:138 -ALC_INVALID_ENUM = 40963 # /usr/include/AL/alc.h:143 -ALC_INVALID_VALUE = 40964 # /usr/include/AL/alc.h:148 -ALC_OUT_OF_MEMORY = 40965 # /usr/include/AL/alc.h:153 -ALC_DEFAULT_DEVICE_SPECIFIER = 4100 # /usr/include/AL/alc.h:159 -ALC_DEVICE_SPECIFIER = 4101 # /usr/include/AL/alc.h:160 -ALC_EXTENSIONS = 4102 # /usr/include/AL/alc.h:161 -ALC_MAJOR_VERSION = 4096 # /usr/include/AL/alc.h:163 -ALC_MINOR_VERSION = 4097 # /usr/include/AL/alc.h:164 -ALC_ATTRIBUTES_SIZE = 4098 # /usr/include/AL/alc.h:166 -ALC_ALL_ATTRIBUTES = 4099 # /usr/include/AL/alc.h:167 -ALC_CAPTURE_DEVICE_SPECIFIER = 784 # /usr/include/AL/alc.h:172 -ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER = 785 # /usr/include/AL/alc.h:173 -ALC_CAPTURE_SAMPLES = 786 # /usr/include/AL/alc.h:174 +ALCcontext = struct_ALCcontext_struct # /usr/include/AL/alc.h:45 +ALCboolean = c_char # /usr/include/AL/alc.h:49 +ALCchar = c_char # /usr/include/AL/alc.h:52 +ALCbyte = c_char # /usr/include/AL/alc.h:55 +ALCubyte = c_ubyte # /usr/include/AL/alc.h:58 +ALCshort = c_short # /usr/include/AL/alc.h:61 +ALCushort = c_ushort # /usr/include/AL/alc.h:64 +ALCint = c_int # /usr/include/AL/alc.h:67 +ALCuint = c_uint # /usr/include/AL/alc.h:70 +ALCsizei = c_int # /usr/include/AL/alc.h:73 +ALCenum = c_int # /usr/include/AL/alc.h:76 +ALCfloat = c_float # /usr/include/AL/alc.h:79 +ALCdouble = c_double # /usr/include/AL/alc.h:82 +ALCvoid = None # /usr/include/AL/alc.h:85 +ALC_FALSE = 0 # /usr/include/AL/alc.h:91 +ALC_TRUE = 1 # /usr/include/AL/alc.h:94 +ALC_FREQUENCY = 4103 # /usr/include/AL/alc.h:99 +ALC_REFRESH = 4104 # /usr/include/AL/alc.h:104 +ALC_SYNC = 4105 # /usr/include/AL/alc.h:109 +ALC_MONO_SOURCES = 4112 # /usr/include/AL/alc.h:114 +ALC_STEREO_SOURCES = 4113 # /usr/include/AL/alc.h:119 +ALC_NO_ERROR = 0 # /usr/include/AL/alc.h:128 +ALC_INVALID_DEVICE = 40961 # /usr/include/AL/alc.h:133 +ALC_INVALID_CONTEXT = 40962 # /usr/include/AL/alc.h:138 +ALC_INVALID_ENUM = 40963 # /usr/include/AL/alc.h:143 +ALC_INVALID_VALUE = 40964 # /usr/include/AL/alc.h:148 +ALC_OUT_OF_MEMORY = 40965 # /usr/include/AL/alc.h:153 +ALC_DEFAULT_DEVICE_SPECIFIER = 4100 # /usr/include/AL/alc.h:159 +ALC_DEVICE_SPECIFIER = 4101 # /usr/include/AL/alc.h:160 +ALC_EXTENSIONS = 4102 # /usr/include/AL/alc.h:161 +ALC_MAJOR_VERSION = 4096 # /usr/include/AL/alc.h:163 +ALC_MINOR_VERSION = 4097 # /usr/include/AL/alc.h:164 +ALC_ATTRIBUTES_SIZE = 4098 # /usr/include/AL/alc.h:166 +ALC_ALL_ATTRIBUTES = 4099 # /usr/include/AL/alc.h:167 +ALC_CAPTURE_DEVICE_SPECIFIER = 784 # /usr/include/AL/alc.h:172 +ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER = 785 # /usr/include/AL/alc.h:173 +ALC_CAPTURE_SAMPLES = 786 # /usr/include/AL/alc.h:174 # /usr/include/AL/alc.h:180 alcCreateContext = _lib.alcCreateContext alcCreateContext.restype = POINTER(ALCcontext) @@ -240,48 +251,48 @@ alcCaptureSamples.restype = None alcCaptureSamples.argtypes = [POINTER(ALCdevice), POINTER(ALCvoid), ALCsizei] -LPALCCREATECONTEXT = CFUNCTYPE(POINTER(ALCcontext), POINTER(ALCdevice), POINTER(ALCint)) # /usr/include/AL/alc.h:246 -LPALCMAKECONTEXTCURRENT = CFUNCTYPE(ALCboolean, POINTER(ALCcontext)) # /usr/include/AL/alc.h:247 -LPALCPROCESSCONTEXT = CFUNCTYPE(None, POINTER(ALCcontext)) # /usr/include/AL/alc.h:248 -LPALCSUSPENDCONTEXT = CFUNCTYPE(None, POINTER(ALCcontext)) # /usr/include/AL/alc.h:249 -LPALCDESTROYCONTEXT = CFUNCTYPE(None, POINTER(ALCcontext)) # /usr/include/AL/alc.h:250 -LPALCGETCURRENTCONTEXT = CFUNCTYPE(POINTER(ALCcontext)) # /usr/include/AL/alc.h:251 -LPALCGETCONTEXTSDEVICE = CFUNCTYPE(POINTER(ALCdevice), POINTER(ALCcontext)) # /usr/include/AL/alc.h:252 -LPALCOPENDEVICE = CFUNCTYPE(POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:253 -LPALCCLOSEDEVICE = CFUNCTYPE(ALCboolean, POINTER(ALCdevice)) # /usr/include/AL/alc.h:254 -LPALCGETERROR = CFUNCTYPE(ALCenum, POINTER(ALCdevice)) # /usr/include/AL/alc.h:255 -LPALCISEXTENSIONPRESENT = CFUNCTYPE(ALCboolean, POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:256 -LPALCGETPROCADDRESS = CFUNCTYPE(POINTER(c_void), POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:257 -LPALCGETENUMVALUE = CFUNCTYPE(ALCenum, POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:258 -LPALCGETSTRING = CFUNCTYPE(POINTER(ALCchar), POINTER(ALCdevice), ALCenum) # /usr/include/AL/alc.h:259 -LPALCGETINTEGERV = CFUNCTYPE(None, POINTER(ALCdevice), ALCenum, ALCsizei, POINTER(ALCint)) # /usr/include/AL/alc.h:260 -LPALCCAPTUREOPENDEVICE = CFUNCTYPE(POINTER(ALCdevice), POINTER(ALCchar), ALCuint, ALCenum, ALCsizei) # /usr/include/AL/alc.h:261 -LPALCCAPTURECLOSEDEVICE = CFUNCTYPE(ALCboolean, POINTER(ALCdevice)) # /usr/include/AL/alc.h:262 -LPALCCAPTURESTART = CFUNCTYPE(None, POINTER(ALCdevice)) # /usr/include/AL/alc.h:263 -LPALCCAPTURESTOP = CFUNCTYPE(None, POINTER(ALCdevice)) # /usr/include/AL/alc.h:264 -LPALCCAPTURESAMPLES = CFUNCTYPE(None, POINTER(ALCdevice), POINTER(ALCvoid), ALCsizei) # /usr/include/AL/alc.h:265 +LPALCCREATECONTEXT = CFUNCTYPE(POINTER(ALCcontext), POINTER(ALCdevice), POINTER(ALCint)) # /usr/include/AL/alc.h:246 +LPALCMAKECONTEXTCURRENT = CFUNCTYPE(ALCboolean, POINTER(ALCcontext)) # /usr/include/AL/alc.h:247 +LPALCPROCESSCONTEXT = CFUNCTYPE(None, POINTER(ALCcontext)) # /usr/include/AL/alc.h:248 +LPALCSUSPENDCONTEXT = CFUNCTYPE(None, POINTER(ALCcontext)) # /usr/include/AL/alc.h:249 +LPALCDESTROYCONTEXT = CFUNCTYPE(None, POINTER(ALCcontext)) # /usr/include/AL/alc.h:250 +LPALCGETCURRENTCONTEXT = CFUNCTYPE(POINTER(ALCcontext)) # /usr/include/AL/alc.h:251 +LPALCGETCONTEXTSDEVICE = CFUNCTYPE(POINTER(ALCdevice), POINTER(ALCcontext)) # /usr/include/AL/alc.h:252 +LPALCOPENDEVICE = CFUNCTYPE(POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:253 +LPALCCLOSEDEVICE = CFUNCTYPE(ALCboolean, POINTER(ALCdevice)) # /usr/include/AL/alc.h:254 +LPALCGETERROR = CFUNCTYPE(ALCenum, POINTER(ALCdevice)) # /usr/include/AL/alc.h:255 +LPALCISEXTENSIONPRESENT = CFUNCTYPE(ALCboolean, POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:256 +LPALCGETPROCADDRESS = CFUNCTYPE(POINTER(c_void), POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:257 +LPALCGETENUMVALUE = CFUNCTYPE(ALCenum, POINTER(ALCdevice), POINTER(ALCchar)) # /usr/include/AL/alc.h:258 +LPALCGETSTRING = CFUNCTYPE(POINTER(ALCchar), POINTER(ALCdevice), ALCenum) # /usr/include/AL/alc.h:259 +LPALCGETINTEGERV = CFUNCTYPE(None, POINTER(ALCdevice), ALCenum, ALCsizei, POINTER(ALCint)) # /usr/include/AL/alc.h:260 +LPALCCAPTUREOPENDEVICE = CFUNCTYPE(POINTER(ALCdevice), POINTER(ALCchar), ALCuint, ALCenum, ALCsizei) # /usr/include/AL/alc.h:261 +LPALCCAPTURECLOSEDEVICE = CFUNCTYPE(ALCboolean, POINTER(ALCdevice)) # /usr/include/AL/alc.h:262 +LPALCCAPTURESTART = CFUNCTYPE(None, POINTER(ALCdevice)) # /usr/include/AL/alc.h:263 +LPALCCAPTURESTOP = CFUNCTYPE(None, POINTER(ALCdevice)) # /usr/include/AL/alc.h:264 +LPALCCAPTURESAMPLES = CFUNCTYPE(None, POINTER(ALCdevice), POINTER(ALCvoid), ALCsizei) # /usr/include/AL/alc.h:265 __all__ = ['ALC_API', 'ALCAPI', 'ALC_INVALID', 'ALC_VERSION_0_1', 'ALCdevice', -'ALCcontext', 'ALCboolean', 'ALCchar', 'ALCbyte', 'ALCubyte', 'ALCshort', -'ALCushort', 'ALCint', 'ALCuint', 'ALCsizei', 'ALCenum', 'ALCfloat', -'ALCdouble', 'ALCvoid', 'ALC_FALSE', 'ALC_TRUE', 'ALC_FREQUENCY', -'ALC_REFRESH', 'ALC_SYNC', 'ALC_MONO_SOURCES', 'ALC_STEREO_SOURCES', -'ALC_NO_ERROR', 'ALC_INVALID_DEVICE', 'ALC_INVALID_CONTEXT', -'ALC_INVALID_ENUM', 'ALC_INVALID_VALUE', 'ALC_OUT_OF_MEMORY', -'ALC_DEFAULT_DEVICE_SPECIFIER', 'ALC_DEVICE_SPECIFIER', 'ALC_EXTENSIONS', -'ALC_MAJOR_VERSION', 'ALC_MINOR_VERSION', 'ALC_ATTRIBUTES_SIZE', -'ALC_ALL_ATTRIBUTES', 'ALC_CAPTURE_DEVICE_SPECIFIER', -'ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER', 'ALC_CAPTURE_SAMPLES', -'alcCreateContext', 'alcMakeContextCurrent', 'alcProcessContext', -'alcSuspendContext', 'alcDestroyContext', 'alcGetCurrentContext', -'alcGetContextsDevice', 'alcOpenDevice', 'alcCloseDevice', 'alcGetError', -'alcIsExtensionPresent', 'alcGetProcAddress', 'alcGetEnumValue', -'alcGetString', 'alcGetIntegerv', 'alcCaptureOpenDevice', -'alcCaptureCloseDevice', 'alcCaptureStart', 'alcCaptureStop', -'alcCaptureSamples', 'LPALCCREATECONTEXT', 'LPALCMAKECONTEXTCURRENT', -'LPALCPROCESSCONTEXT', 'LPALCSUSPENDCONTEXT', 'LPALCDESTROYCONTEXT', -'LPALCGETCURRENTCONTEXT', 'LPALCGETCONTEXTSDEVICE', 'LPALCOPENDEVICE', -'LPALCCLOSEDEVICE', 'LPALCGETERROR', 'LPALCISEXTENSIONPRESENT', -'LPALCGETPROCADDRESS', 'LPALCGETENUMVALUE', 'LPALCGETSTRING', -'LPALCGETINTEGERV', 'LPALCCAPTUREOPENDEVICE', 'LPALCCAPTURECLOSEDEVICE', -'LPALCCAPTURESTART', 'LPALCCAPTURESTOP', 'LPALCCAPTURESAMPLES'] + 'ALCcontext', 'ALCboolean', 'ALCchar', 'ALCbyte', 'ALCubyte', 'ALCshort', + 'ALCushort', 'ALCint', 'ALCuint', 'ALCsizei', 'ALCenum', 'ALCfloat', + 'ALCdouble', 'ALCvoid', 'ALC_FALSE', 'ALC_TRUE', 'ALC_FREQUENCY', + 'ALC_REFRESH', 'ALC_SYNC', 'ALC_MONO_SOURCES', 'ALC_STEREO_SOURCES', + 'ALC_NO_ERROR', 'ALC_INVALID_DEVICE', 'ALC_INVALID_CONTEXT', + 'ALC_INVALID_ENUM', 'ALC_INVALID_VALUE', 'ALC_OUT_OF_MEMORY', + 'ALC_DEFAULT_DEVICE_SPECIFIER', 'ALC_DEVICE_SPECIFIER', 'ALC_EXTENSIONS', + 'ALC_MAJOR_VERSION', 'ALC_MINOR_VERSION', 'ALC_ATTRIBUTES_SIZE', + 'ALC_ALL_ATTRIBUTES', 'ALC_CAPTURE_DEVICE_SPECIFIER', + 'ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER', 'ALC_CAPTURE_SAMPLES', + 'alcCreateContext', 'alcMakeContextCurrent', 'alcProcessContext', + 'alcSuspendContext', 'alcDestroyContext', 'alcGetCurrentContext', + 'alcGetContextsDevice', 'alcOpenDevice', 'alcCloseDevice', 'alcGetError', + 'alcIsExtensionPresent', 'alcGetProcAddress', 'alcGetEnumValue', + 'alcGetString', 'alcGetIntegerv', 'alcCaptureOpenDevice', + 'alcCaptureCloseDevice', 'alcCaptureStart', 'alcCaptureStop', + 'alcCaptureSamples', 'LPALCCREATECONTEXT', 'LPALCMAKECONTEXTCURRENT', + 'LPALCPROCESSCONTEXT', 'LPALCSUSPENDCONTEXT', 'LPALCDESTROYCONTEXT', + 'LPALCGETCURRENTCONTEXT', 'LPALCGETCONTEXTSDEVICE', 'LPALCOPENDEVICE', + 'LPALCCLOSEDEVICE', 'LPALCGETERROR', 'LPALCISEXTENSIONPRESENT', + 'LPALCGETPROCADDRESS', 'LPALCGETENUMVALUE', 'LPALCGETSTRING', + 'LPALCGETINTEGERV', 'LPALCCAPTUREOPENDEVICE', 'LPALCCAPTURECLOSEDEVICE', + 'LPALCCAPTURESTART', 'LPALCCAPTURESTOP', 'LPALCCAPTURESAMPLES'] diff -Nru pyglet-1.4.10/pyglet/media/drivers/openal/lib_openal.py pyglet-1.5.14/pyglet/media/drivers/openal/lib_openal.py --- pyglet-1.4.10/pyglet/media/drivers/openal/lib_openal.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/openal/lib_openal.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,7 +32,7 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for openal +"""Wrapper for openal Generated with: ../tools/wraptypes/wrap.py /usr/include/AL/al.h -lopenal -olib_openal.py @@ -43,19 +43,16 @@ .. alListener3i and alListeneriv are present in my OS X 10.4 but not another 10.4 user's installation. They've also been removed for compatibility. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" import ctypes from ctypes import * -import sys import pyglet.lib -_lib = pyglet.lib.load_library('openal', win32='openal32', - framework='/System/Library/Frameworks/OpenAL.framework') +_lib = pyglet.lib.load_library('openal', + win32='openal32', + framework='OpenAL') _int_types = (c_int16, c_int32) if hasattr(ctypes, 'c_int64'): @@ -67,6 +64,7 @@ if sizeof(t) == sizeof(c_size_t): c_ptrdiff_t = t + class c_void(Structure): # c_void_p is a buggy return type, converting to int, so # POINTER(None) == c_void_p is actually written as @@ -74,90 +72,89 @@ _fields_ = [('dummy', c_int)] - -AL_API = 0 # /usr/include/AL/al.h:39 -ALAPI = 0 # /usr/include/AL/al.h:59 -AL_INVALID = -1 # /usr/include/AL/al.h:61 -AL_ILLEGAL_ENUM = 0 # /usr/include/AL/al.h:62 -AL_ILLEGAL_COMMAND = 0 # /usr/include/AL/al.h:63 -ALboolean = c_int # Better return type than c_char, as generated -ALchar = c_char # /usr/include/AL/al.h:73 -ALbyte = c_char # /usr/include/AL/al.h:76 -ALubyte = c_ubyte # /usr/include/AL/al.h:79 -ALshort = c_short # /usr/include/AL/al.h:82 -ALushort = c_ushort # /usr/include/AL/al.h:85 -ALint = c_int # /usr/include/AL/al.h:88 -ALuint = c_uint # /usr/include/AL/al.h:91 -ALsizei = c_int # /usr/include/AL/al.h:94 -ALenum = c_int # /usr/include/AL/al.h:97 -ALfloat = c_float # /usr/include/AL/al.h:100 -ALdouble = c_double # /usr/include/AL/al.h:103 -ALvoid = None # /usr/include/AL/al.h:106 -AL_NONE = 0 # /usr/include/AL/al.h:112 -AL_FALSE = 0 # /usr/include/AL/al.h:115 -AL_TRUE = 1 # /usr/include/AL/al.h:118 -AL_SOURCE_RELATIVE = 514 # /usr/include/AL/al.h:121 -AL_CONE_INNER_ANGLE = 4097 # /usr/include/AL/al.h:130 -AL_CONE_OUTER_ANGLE = 4098 # /usr/include/AL/al.h:137 -AL_PITCH = 4099 # /usr/include/AL/al.h:145 -AL_POSITION = 4100 # /usr/include/AL/al.h:157 -AL_DIRECTION = 4101 # /usr/include/AL/al.h:160 -AL_VELOCITY = 4102 # /usr/include/AL/al.h:163 -AL_LOOPING = 4103 # /usr/include/AL/al.h:171 -AL_BUFFER = 4105 # /usr/include/AL/al.h:178 -AL_GAIN = 4106 # /usr/include/AL/al.h:191 -AL_MIN_GAIN = 4109 # /usr/include/AL/al.h:200 -AL_MAX_GAIN = 4110 # /usr/include/AL/al.h:209 -AL_ORIENTATION = 4111 # /usr/include/AL/al.h:216 -AL_SOURCE_STATE = 4112 # /usr/include/AL/al.h:221 -AL_INITIAL = 4113 # /usr/include/AL/al.h:222 -AL_PLAYING = 4114 # /usr/include/AL/al.h:223 -AL_PAUSED = 4115 # /usr/include/AL/al.h:224 -AL_STOPPED = 4116 # /usr/include/AL/al.h:225 -AL_BUFFERS_QUEUED = 4117 # /usr/include/AL/al.h:230 -AL_BUFFERS_PROCESSED = 4118 # /usr/include/AL/al.h:231 -AL_SEC_OFFSET = 4132 # /usr/include/AL/al.h:236 -AL_SAMPLE_OFFSET = 4133 # /usr/include/AL/al.h:237 -AL_BYTE_OFFSET = 4134 # /usr/include/AL/al.h:238 -AL_SOURCE_TYPE = 4135 # /usr/include/AL/al.h:246 -AL_STATIC = 4136 # /usr/include/AL/al.h:247 -AL_STREAMING = 4137 # /usr/include/AL/al.h:248 -AL_UNDETERMINED = 4144 # /usr/include/AL/al.h:249 -AL_FORMAT_MONO8 = 4352 # /usr/include/AL/al.h:252 -AL_FORMAT_MONO16 = 4353 # /usr/include/AL/al.h:253 -AL_FORMAT_STEREO8 = 4354 # /usr/include/AL/al.h:254 -AL_FORMAT_STEREO16 = 4355 # /usr/include/AL/al.h:255 -AL_REFERENCE_DISTANCE = 4128 # /usr/include/AL/al.h:265 -AL_ROLLOFF_FACTOR = 4129 # /usr/include/AL/al.h:273 -AL_CONE_OUTER_GAIN = 4130 # /usr/include/AL/al.h:282 -AL_MAX_DISTANCE = 4131 # /usr/include/AL/al.h:292 -AL_FREQUENCY = 8193 # /usr/include/AL/al.h:300 -AL_BITS = 8194 # /usr/include/AL/al.h:301 -AL_CHANNELS = 8195 # /usr/include/AL/al.h:302 -AL_SIZE = 8196 # /usr/include/AL/al.h:303 -AL_UNUSED = 8208 # /usr/include/AL/al.h:310 -AL_PENDING = 8209 # /usr/include/AL/al.h:311 -AL_PROCESSED = 8210 # /usr/include/AL/al.h:312 -AL_NO_ERROR = 0 # /usr/include/AL/al.h:316 -AL_INVALID_NAME = 40961 # /usr/include/AL/al.h:321 -AL_INVALID_ENUM = 40962 # /usr/include/AL/al.h:326 -AL_INVALID_VALUE = 40963 # /usr/include/AL/al.h:331 -AL_INVALID_OPERATION = 40964 # /usr/include/AL/al.h:336 -AL_OUT_OF_MEMORY = 40965 # /usr/include/AL/al.h:342 -AL_VENDOR = 45057 # /usr/include/AL/al.h:346 -AL_VERSION = 45058 # /usr/include/AL/al.h:347 -AL_RENDERER = 45059 # /usr/include/AL/al.h:348 -AL_EXTENSIONS = 45060 # /usr/include/AL/al.h:349 -AL_DOPPLER_FACTOR = 49152 # /usr/include/AL/al.h:356 -AL_DOPPLER_VELOCITY = 49153 # /usr/include/AL/al.h:361 -AL_SPEED_OF_SOUND = 49155 # /usr/include/AL/al.h:366 -AL_DISTANCE_MODEL = 53248 # /usr/include/AL/al.h:375 -AL_INVERSE_DISTANCE = 53249 # /usr/include/AL/al.h:376 -AL_INVERSE_DISTANCE_CLAMPED = 53250 # /usr/include/AL/al.h:377 -AL_LINEAR_DISTANCE = 53251 # /usr/include/AL/al.h:378 -AL_LINEAR_DISTANCE_CLAMPED = 53252 # /usr/include/AL/al.h:379 -AL_EXPONENT_DISTANCE = 53253 # /usr/include/AL/al.h:380 -AL_EXPONENT_DISTANCE_CLAMPED = 53254 # /usr/include/AL/al.h:381 +AL_API = 0 # /usr/include/AL/al.h:39 +ALAPI = 0 # /usr/include/AL/al.h:59 +AL_INVALID = -1 # /usr/include/AL/al.h:61 +AL_ILLEGAL_ENUM = 0 # /usr/include/AL/al.h:62 +AL_ILLEGAL_COMMAND = 0 # /usr/include/AL/al.h:63 +ALboolean = c_int # Better return type than c_char, as generated +ALchar = c_char # /usr/include/AL/al.h:73 +ALbyte = c_char # /usr/include/AL/al.h:76 +ALubyte = c_ubyte # /usr/include/AL/al.h:79 +ALshort = c_short # /usr/include/AL/al.h:82 +ALushort = c_ushort # /usr/include/AL/al.h:85 +ALint = c_int # /usr/include/AL/al.h:88 +ALuint = c_uint # /usr/include/AL/al.h:91 +ALsizei = c_int # /usr/include/AL/al.h:94 +ALenum = c_int # /usr/include/AL/al.h:97 +ALfloat = c_float # /usr/include/AL/al.h:100 +ALdouble = c_double # /usr/include/AL/al.h:103 +ALvoid = None # /usr/include/AL/al.h:106 +AL_NONE = 0 # /usr/include/AL/al.h:112 +AL_FALSE = 0 # /usr/include/AL/al.h:115 +AL_TRUE = 1 # /usr/include/AL/al.h:118 +AL_SOURCE_RELATIVE = 514 # /usr/include/AL/al.h:121 +AL_CONE_INNER_ANGLE = 4097 # /usr/include/AL/al.h:130 +AL_CONE_OUTER_ANGLE = 4098 # /usr/include/AL/al.h:137 +AL_PITCH = 4099 # /usr/include/AL/al.h:145 +AL_POSITION = 4100 # /usr/include/AL/al.h:157 +AL_DIRECTION = 4101 # /usr/include/AL/al.h:160 +AL_VELOCITY = 4102 # /usr/include/AL/al.h:163 +AL_LOOPING = 4103 # /usr/include/AL/al.h:171 +AL_BUFFER = 4105 # /usr/include/AL/al.h:178 +AL_GAIN = 4106 # /usr/include/AL/al.h:191 +AL_MIN_GAIN = 4109 # /usr/include/AL/al.h:200 +AL_MAX_GAIN = 4110 # /usr/include/AL/al.h:209 +AL_ORIENTATION = 4111 # /usr/include/AL/al.h:216 +AL_SOURCE_STATE = 4112 # /usr/include/AL/al.h:221 +AL_INITIAL = 4113 # /usr/include/AL/al.h:222 +AL_PLAYING = 4114 # /usr/include/AL/al.h:223 +AL_PAUSED = 4115 # /usr/include/AL/al.h:224 +AL_STOPPED = 4116 # /usr/include/AL/al.h:225 +AL_BUFFERS_QUEUED = 4117 # /usr/include/AL/al.h:230 +AL_BUFFERS_PROCESSED = 4118 # /usr/include/AL/al.h:231 +AL_SEC_OFFSET = 4132 # /usr/include/AL/al.h:236 +AL_SAMPLE_OFFSET = 4133 # /usr/include/AL/al.h:237 +AL_BYTE_OFFSET = 4134 # /usr/include/AL/al.h:238 +AL_SOURCE_TYPE = 4135 # /usr/include/AL/al.h:246 +AL_STATIC = 4136 # /usr/include/AL/al.h:247 +AL_STREAMING = 4137 # /usr/include/AL/al.h:248 +AL_UNDETERMINED = 4144 # /usr/include/AL/al.h:249 +AL_FORMAT_MONO8 = 4352 # /usr/include/AL/al.h:252 +AL_FORMAT_MONO16 = 4353 # /usr/include/AL/al.h:253 +AL_FORMAT_STEREO8 = 4354 # /usr/include/AL/al.h:254 +AL_FORMAT_STEREO16 = 4355 # /usr/include/AL/al.h:255 +AL_REFERENCE_DISTANCE = 4128 # /usr/include/AL/al.h:265 +AL_ROLLOFF_FACTOR = 4129 # /usr/include/AL/al.h:273 +AL_CONE_OUTER_GAIN = 4130 # /usr/include/AL/al.h:282 +AL_MAX_DISTANCE = 4131 # /usr/include/AL/al.h:292 +AL_FREQUENCY = 8193 # /usr/include/AL/al.h:300 +AL_BITS = 8194 # /usr/include/AL/al.h:301 +AL_CHANNELS = 8195 # /usr/include/AL/al.h:302 +AL_SIZE = 8196 # /usr/include/AL/al.h:303 +AL_UNUSED = 8208 # /usr/include/AL/al.h:310 +AL_PENDING = 8209 # /usr/include/AL/al.h:311 +AL_PROCESSED = 8210 # /usr/include/AL/al.h:312 +AL_NO_ERROR = 0 # /usr/include/AL/al.h:316 +AL_INVALID_NAME = 40961 # /usr/include/AL/al.h:321 +AL_INVALID_ENUM = 40962 # /usr/include/AL/al.h:326 +AL_INVALID_VALUE = 40963 # /usr/include/AL/al.h:331 +AL_INVALID_OPERATION = 40964 # /usr/include/AL/al.h:336 +AL_OUT_OF_MEMORY = 40965 # /usr/include/AL/al.h:342 +AL_VENDOR = 45057 # /usr/include/AL/al.h:346 +AL_VERSION = 45058 # /usr/include/AL/al.h:347 +AL_RENDERER = 45059 # /usr/include/AL/al.h:348 +AL_EXTENSIONS = 45060 # /usr/include/AL/al.h:349 +AL_DOPPLER_FACTOR = 49152 # /usr/include/AL/al.h:356 +AL_DOPPLER_VELOCITY = 49153 # /usr/include/AL/al.h:361 +AL_SPEED_OF_SOUND = 49155 # /usr/include/AL/al.h:366 +AL_DISTANCE_MODEL = 53248 # /usr/include/AL/al.h:375 +AL_INVERSE_DISTANCE = 53249 # /usr/include/AL/al.h:376 +AL_INVERSE_DISTANCE_CLAMPED = 53250 # /usr/include/AL/al.h:377 +AL_LINEAR_DISTANCE = 53251 # /usr/include/AL/al.h:378 +AL_LINEAR_DISTANCE_CLAMPED = 53252 # /usr/include/AL/al.h:379 +AL_EXPONENT_DISTANCE = 53253 # /usr/include/AL/al.h:380 +AL_EXPONENT_DISTANCE_CLAMPED = 53254 # /usr/include/AL/al.h:381 # /usr/include/AL/al.h:386 alEnable = _lib.alEnable alEnable.restype = None @@ -259,14 +256,14 @@ alListeneri.argtypes = [ALenum, ALint] # /usr/include/AL/al.h:458 -#alListener3i = _lib.alListener3i -#alListener3i.restype = None -#alListener3i.argtypes = [ALenum, ALint, ALint, ALint] +# alListener3i = _lib.alListener3i +# alListener3i.restype = None +# alListener3i.argtypes = [ALenum, ALint, ALint, ALint] # /usr/include/AL/al.h:460 -#alListeneriv = _lib.alListeneriv -#alListeneriv.restype = None -#alListeneriv.argtypes = [ALenum, POINTER(ALint)] +# alListeneriv = _lib.alListeneriv +# alListeneriv.restype = None +# alListeneriv.argtypes = [ALenum, POINTER(ALint)] # /usr/include/AL/al.h:465 alGetListenerf = _lib.alGetListenerf @@ -334,14 +331,14 @@ alSourcei.argtypes = [ALuint, ALenum, ALint] # /usr/include/AL/al.h:531 -#alSource3i = _lib.alSource3i -#alSource3i.restype = None -#alSource3i.argtypes = [ALuint, ALenum, ALint, ALint, ALint] +# alSource3i = _lib.alSource3i +# alSource3i.restype = None +# alSource3i.argtypes = [ALuint, ALenum, ALint, ALint, ALint] # /usr/include/AL/al.h:533 -#alSourceiv = _lib.alSourceiv -#alSourceiv.restype = None -#alSourceiv.argtypes = [ALuint, ALenum, POINTER(ALint)] +# alSourceiv = _lib.alSourceiv +# alSourceiv.restype = None +# alSourceiv.argtypes = [ALuint, ALenum, POINTER(ALint)] # /usr/include/AL/al.h:538 alGetSourcef = _lib.alGetSourcef @@ -364,9 +361,9 @@ alGetSourcei.argtypes = [ALuint, ALenum, POINTER(ALint)] # /usr/include/AL/al.h:546 -#alGetSource3i = _lib.alGetSource3i -#alGetSource3i.restype = None -#alGetSource3i.argtypes = [ALuint, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)] +# alGetSource3i = _lib.alGetSource3i +# alGetSource3i.restype = None +# alGetSource3i.argtypes = [ALuint, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)] # /usr/include/AL/al.h:548 alGetSourceiv = _lib.alGetSourceiv @@ -523,134 +520,134 @@ alDistanceModel.restype = None alDistanceModel.argtypes = [ALenum] -LPALENABLE = CFUNCTYPE(None, ALenum) # /usr/include/AL/al.h:662 -LPALDISABLE = CFUNCTYPE(None, ALenum) # /usr/include/AL/al.h:663 -LPALISENABLED = CFUNCTYPE(ALboolean, ALenum) # /usr/include/AL/al.h:664 -LPALGETSTRING = CFUNCTYPE(POINTER(ALchar), ALenum) # /usr/include/AL/al.h:665 -LPALGETBOOLEANV = CFUNCTYPE(None, ALenum, POINTER(ALboolean)) # /usr/include/AL/al.h:666 -LPALGETINTEGERV = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:667 -LPALGETFLOATV = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:668 -LPALGETDOUBLEV = CFUNCTYPE(None, ALenum, POINTER(ALdouble)) # /usr/include/AL/al.h:669 -LPALGETBOOLEAN = CFUNCTYPE(ALboolean, ALenum) # /usr/include/AL/al.h:670 -LPALGETINTEGER = CFUNCTYPE(ALint, ALenum) # /usr/include/AL/al.h:671 -LPALGETFLOAT = CFUNCTYPE(ALfloat, ALenum) # /usr/include/AL/al.h:672 -LPALGETDOUBLE = CFUNCTYPE(ALdouble, ALenum) # /usr/include/AL/al.h:673 -LPALGETERROR = CFUNCTYPE(ALenum) # /usr/include/AL/al.h:674 -LPALISEXTENSIONPRESENT = CFUNCTYPE(ALboolean, POINTER(ALchar)) # /usr/include/AL/al.h:675 -LPALGETPROCADDRESS = CFUNCTYPE(POINTER(c_void), POINTER(ALchar)) # /usr/include/AL/al.h:676 -LPALGETENUMVALUE = CFUNCTYPE(ALenum, POINTER(ALchar)) # /usr/include/AL/al.h:677 -LPALLISTENERF = CFUNCTYPE(None, ALenum, ALfloat) # /usr/include/AL/al.h:678 -LPALLISTENER3F = CFUNCTYPE(None, ALenum, ALfloat, ALfloat, ALfloat) # /usr/include/AL/al.h:679 -LPALLISTENERFV = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:680 -LPALLISTENERI = CFUNCTYPE(None, ALenum, ALint) # /usr/include/AL/al.h:681 -LPALLISTENER3I = CFUNCTYPE(None, ALenum, ALint, ALint, ALint) # /usr/include/AL/al.h:682 -LPALLISTENERIV = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:683 -LPALGETLISTENERF = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:684 -LPALGETLISTENER3F = CFUNCTYPE(None, ALenum, POINTER(ALfloat), POINTER(ALfloat), POINTER(ALfloat)) # /usr/include/AL/al.h:685 -LPALGETLISTENERFV = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:686 -LPALGETLISTENERI = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:687 -LPALGETLISTENER3I = CFUNCTYPE(None, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)) # /usr/include/AL/al.h:688 -LPALGETLISTENERIV = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:689 -LPALGENSOURCES = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:690 -LPALDELETESOURCES = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:691 -LPALISSOURCE = CFUNCTYPE(ALboolean, ALuint) # /usr/include/AL/al.h:692 -LPALSOURCEF = CFUNCTYPE(None, ALuint, ALenum, ALfloat) # /usr/include/AL/al.h:693 -LPALSOURCE3F = CFUNCTYPE(None, ALuint, ALenum, ALfloat, ALfloat, ALfloat) # /usr/include/AL/al.h:694 -LPALSOURCEFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:695 -LPALSOURCEI = CFUNCTYPE(None, ALuint, ALenum, ALint) # /usr/include/AL/al.h:696 -LPALSOURCE3I = CFUNCTYPE(None, ALuint, ALenum, ALint, ALint, ALint) # /usr/include/AL/al.h:697 -LPALSOURCEIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:698 -LPALGETSOURCEF = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:699 -LPALGETSOURCE3F = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat), POINTER(ALfloat), POINTER(ALfloat)) # /usr/include/AL/al.h:700 -LPALGETSOURCEFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:701 -LPALGETSOURCEI = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:702 -LPALGETSOURCE3I = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)) # /usr/include/AL/al.h:703 -LPALGETSOURCEIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:704 -LPALSOURCEPLAYV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:705 -LPALSOURCESTOPV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:706 -LPALSOURCEREWINDV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:707 -LPALSOURCEPAUSEV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:708 -LPALSOURCEPLAY = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:709 -LPALSOURCESTOP = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:710 -LPALSOURCEREWIND = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:711 -LPALSOURCEPAUSE = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:712 -LPALSOURCEQUEUEBUFFERS = CFUNCTYPE(None, ALuint, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:713 -LPALSOURCEUNQUEUEBUFFERS = CFUNCTYPE(None, ALuint, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:714 -LPALGENBUFFERS = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:715 -LPALDELETEBUFFERS = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:716 -LPALISBUFFER = CFUNCTYPE(ALboolean, ALuint) # /usr/include/AL/al.h:717 -LPALBUFFERDATA = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALvoid), ALsizei, ALsizei) # /usr/include/AL/al.h:718 -LPALBUFFERF = CFUNCTYPE(None, ALuint, ALenum, ALfloat) # /usr/include/AL/al.h:719 -LPALBUFFER3F = CFUNCTYPE(None, ALuint, ALenum, ALfloat, ALfloat, ALfloat) # /usr/include/AL/al.h:720 -LPALBUFFERFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:721 -LPALBUFFERI = CFUNCTYPE(None, ALuint, ALenum, ALint) # /usr/include/AL/al.h:722 -LPALBUFFER3I = CFUNCTYPE(None, ALuint, ALenum, ALint, ALint, ALint) # /usr/include/AL/al.h:723 -LPALBUFFERIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:724 -LPALGETBUFFERF = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:725 -LPALGETBUFFER3F = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat), POINTER(ALfloat), POINTER(ALfloat)) # /usr/include/AL/al.h:726 -LPALGETBUFFERFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:727 -LPALGETBUFFERI = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:728 -LPALGETBUFFER3I = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)) # /usr/include/AL/al.h:729 -LPALGETBUFFERIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:730 -LPALDOPPLERFACTOR = CFUNCTYPE(None, ALfloat) # /usr/include/AL/al.h:731 -LPALDOPPLERVELOCITY = CFUNCTYPE(None, ALfloat) # /usr/include/AL/al.h:732 -LPALSPEEDOFSOUND = CFUNCTYPE(None, ALfloat) # /usr/include/AL/al.h:733 -LPALDISTANCEMODEL = CFUNCTYPE(None, ALenum) # /usr/include/AL/al.h:734 +LPALENABLE = CFUNCTYPE(None, ALenum) # /usr/include/AL/al.h:662 +LPALDISABLE = CFUNCTYPE(None, ALenum) # /usr/include/AL/al.h:663 +LPALISENABLED = CFUNCTYPE(ALboolean, ALenum) # /usr/include/AL/al.h:664 +LPALGETSTRING = CFUNCTYPE(POINTER(ALchar), ALenum) # /usr/include/AL/al.h:665 +LPALGETBOOLEANV = CFUNCTYPE(None, ALenum, POINTER(ALboolean)) # /usr/include/AL/al.h:666 +LPALGETINTEGERV = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:667 +LPALGETFLOATV = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:668 +LPALGETDOUBLEV = CFUNCTYPE(None, ALenum, POINTER(ALdouble)) # /usr/include/AL/al.h:669 +LPALGETBOOLEAN = CFUNCTYPE(ALboolean, ALenum) # /usr/include/AL/al.h:670 +LPALGETINTEGER = CFUNCTYPE(ALint, ALenum) # /usr/include/AL/al.h:671 +LPALGETFLOAT = CFUNCTYPE(ALfloat, ALenum) # /usr/include/AL/al.h:672 +LPALGETDOUBLE = CFUNCTYPE(ALdouble, ALenum) # /usr/include/AL/al.h:673 +LPALGETERROR = CFUNCTYPE(ALenum) # /usr/include/AL/al.h:674 +LPALISEXTENSIONPRESENT = CFUNCTYPE(ALboolean, POINTER(ALchar)) # /usr/include/AL/al.h:675 +LPALGETPROCADDRESS = CFUNCTYPE(POINTER(c_void), POINTER(ALchar)) # /usr/include/AL/al.h:676 +LPALGETENUMVALUE = CFUNCTYPE(ALenum, POINTER(ALchar)) # /usr/include/AL/al.h:677 +LPALLISTENERF = CFUNCTYPE(None, ALenum, ALfloat) # /usr/include/AL/al.h:678 +LPALLISTENER3F = CFUNCTYPE(None, ALenum, ALfloat, ALfloat, ALfloat) # /usr/include/AL/al.h:679 +LPALLISTENERFV = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:680 +LPALLISTENERI = CFUNCTYPE(None, ALenum, ALint) # /usr/include/AL/al.h:681 +LPALLISTENER3I = CFUNCTYPE(None, ALenum, ALint, ALint, ALint) # /usr/include/AL/al.h:682 +LPALLISTENERIV = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:683 +LPALGETLISTENERF = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:684 +LPALGETLISTENER3F = CFUNCTYPE(None, ALenum, POINTER(ALfloat), POINTER(ALfloat), POINTER(ALfloat)) # /usr/include/AL/al.h:685 +LPALGETLISTENERFV = CFUNCTYPE(None, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:686 +LPALGETLISTENERI = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:687 +LPALGETLISTENER3I = CFUNCTYPE(None, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)) # /usr/include/AL/al.h:688 +LPALGETLISTENERIV = CFUNCTYPE(None, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:689 +LPALGENSOURCES = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:690 +LPALDELETESOURCES = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:691 +LPALISSOURCE = CFUNCTYPE(ALboolean, ALuint) # /usr/include/AL/al.h:692 +LPALSOURCEF = CFUNCTYPE(None, ALuint, ALenum, ALfloat) # /usr/include/AL/al.h:693 +LPALSOURCE3F = CFUNCTYPE(None, ALuint, ALenum, ALfloat, ALfloat, ALfloat) # /usr/include/AL/al.h:694 +LPALSOURCEFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:695 +LPALSOURCEI = CFUNCTYPE(None, ALuint, ALenum, ALint) # /usr/include/AL/al.h:696 +LPALSOURCE3I = CFUNCTYPE(None, ALuint, ALenum, ALint, ALint, ALint) # /usr/include/AL/al.h:697 +LPALSOURCEIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:698 +LPALGETSOURCEF = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:699 +LPALGETSOURCE3F = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat), POINTER(ALfloat), POINTER(ALfloat)) # /usr/include/AL/al.h:700 +LPALGETSOURCEFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:701 +LPALGETSOURCEI = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:702 +LPALGETSOURCE3I = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)) # /usr/include/AL/al.h:703 +LPALGETSOURCEIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:704 +LPALSOURCEPLAYV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:705 +LPALSOURCESTOPV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:706 +LPALSOURCEREWINDV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:707 +LPALSOURCEPAUSEV = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:708 +LPALSOURCEPLAY = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:709 +LPALSOURCESTOP = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:710 +LPALSOURCEREWIND = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:711 +LPALSOURCEPAUSE = CFUNCTYPE(None, ALuint) # /usr/include/AL/al.h:712 +LPALSOURCEQUEUEBUFFERS = CFUNCTYPE(None, ALuint, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:713 +LPALSOURCEUNQUEUEBUFFERS = CFUNCTYPE(None, ALuint, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:714 +LPALGENBUFFERS = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:715 +LPALDELETEBUFFERS = CFUNCTYPE(None, ALsizei, POINTER(ALuint)) # /usr/include/AL/al.h:716 +LPALISBUFFER = CFUNCTYPE(ALboolean, ALuint) # /usr/include/AL/al.h:717 +LPALBUFFERDATA = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALvoid), ALsizei, ALsizei) # /usr/include/AL/al.h:718 +LPALBUFFERF = CFUNCTYPE(None, ALuint, ALenum, ALfloat) # /usr/include/AL/al.h:719 +LPALBUFFER3F = CFUNCTYPE(None, ALuint, ALenum, ALfloat, ALfloat, ALfloat) # /usr/include/AL/al.h:720 +LPALBUFFERFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:721 +LPALBUFFERI = CFUNCTYPE(None, ALuint, ALenum, ALint) # /usr/include/AL/al.h:722 +LPALBUFFER3I = CFUNCTYPE(None, ALuint, ALenum, ALint, ALint, ALint) # /usr/include/AL/al.h:723 +LPALBUFFERIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:724 +LPALGETBUFFERF = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:725 +LPALGETBUFFER3F = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat), POINTER(ALfloat), POINTER(ALfloat)) # /usr/include/AL/al.h:726 +LPALGETBUFFERFV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALfloat)) # /usr/include/AL/al.h:727 +LPALGETBUFFERI = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:728 +LPALGETBUFFER3I = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint), POINTER(ALint), POINTER(ALint)) # /usr/include/AL/al.h:729 +LPALGETBUFFERIV = CFUNCTYPE(None, ALuint, ALenum, POINTER(ALint)) # /usr/include/AL/al.h:730 +LPALDOPPLERFACTOR = CFUNCTYPE(None, ALfloat) # /usr/include/AL/al.h:731 +LPALDOPPLERVELOCITY = CFUNCTYPE(None, ALfloat) # /usr/include/AL/al.h:732 +LPALSPEEDOFSOUND = CFUNCTYPE(None, ALfloat) # /usr/include/AL/al.h:733 +LPALDISTANCEMODEL = CFUNCTYPE(None, ALenum) # /usr/include/AL/al.h:734 __all__ = ['AL_API', 'ALAPI', 'AL_INVALID', 'AL_ILLEGAL_ENUM', -'AL_ILLEGAL_COMMAND', 'ALboolean', 'ALchar', 'ALbyte', 'ALubyte', 'ALshort', -'ALushort', 'ALint', 'ALuint', 'ALsizei', 'ALenum', 'ALfloat', 'ALdouble', -'ALvoid', 'AL_NONE', 'AL_FALSE', 'AL_TRUE', 'AL_SOURCE_RELATIVE', -'AL_CONE_INNER_ANGLE', 'AL_CONE_OUTER_ANGLE', 'AL_PITCH', 'AL_POSITION', -'AL_DIRECTION', 'AL_VELOCITY', 'AL_LOOPING', 'AL_BUFFER', 'AL_GAIN', -'AL_MIN_GAIN', 'AL_MAX_GAIN', 'AL_ORIENTATION', 'AL_SOURCE_STATE', -'AL_INITIAL', 'AL_PLAYING', 'AL_PAUSED', 'AL_STOPPED', 'AL_BUFFERS_QUEUED', -'AL_BUFFERS_PROCESSED', 'AL_SEC_OFFSET', 'AL_SAMPLE_OFFSET', 'AL_BYTE_OFFSET', -'AL_SOURCE_TYPE', 'AL_STATIC', 'AL_STREAMING', 'AL_UNDETERMINED', -'AL_FORMAT_MONO8', 'AL_FORMAT_MONO16', 'AL_FORMAT_STEREO8', -'AL_FORMAT_STEREO16', 'AL_REFERENCE_DISTANCE', 'AL_ROLLOFF_FACTOR', -'AL_CONE_OUTER_GAIN', 'AL_MAX_DISTANCE', 'AL_FREQUENCY', 'AL_BITS', -'AL_CHANNELS', 'AL_SIZE', 'AL_UNUSED', 'AL_PENDING', 'AL_PROCESSED', -'AL_NO_ERROR', 'AL_INVALID_NAME', 'AL_INVALID_ENUM', 'AL_INVALID_VALUE', -'AL_INVALID_OPERATION', 'AL_OUT_OF_MEMORY', 'AL_VENDOR', 'AL_VERSION', -'AL_RENDERER', 'AL_EXTENSIONS', 'AL_DOPPLER_FACTOR', 'AL_DOPPLER_VELOCITY', -'AL_SPEED_OF_SOUND', 'AL_DISTANCE_MODEL', 'AL_INVERSE_DISTANCE', -'AL_INVERSE_DISTANCE_CLAMPED', 'AL_LINEAR_DISTANCE', -'AL_LINEAR_DISTANCE_CLAMPED', 'AL_EXPONENT_DISTANCE', -'AL_EXPONENT_DISTANCE_CLAMPED', 'alEnable', 'alDisable', 'alIsEnabled', -'alGetString', 'alGetBooleanv', 'alGetIntegerv', 'alGetFloatv', -'alGetDoublev', 'alGetBoolean', 'alGetInteger', 'alGetFloat', 'alGetDouble', -'alGetError', 'alIsExtensionPresent', 'alGetProcAddress', 'alGetEnumValue', -'alListenerf', 'alListener3f', 'alListenerfv', 'alListeneri', 'alListener3i', -'alListeneriv', 'alGetListenerf', 'alGetListener3f', 'alGetListenerfv', -'alGetListeneri', 'alGetListener3i', 'alGetListeneriv', 'alGenSources', -'alDeleteSources', 'alIsSource', 'alSourcef', 'alSource3f', 'alSourcefv', -'alSourcei', 'alSource3i', 'alSourceiv', 'alGetSourcef', 'alGetSource3f', -'alGetSourcefv', 'alGetSourcei', 'alGetSource3i', 'alGetSourceiv', -'alSourcePlayv', 'alSourceStopv', 'alSourceRewindv', 'alSourcePausev', -'alSourcePlay', 'alSourceStop', 'alSourceRewind', 'alSourcePause', -'alSourceQueueBuffers', 'alSourceUnqueueBuffers', 'alGenBuffers', -'alDeleteBuffers', 'alIsBuffer', 'alBufferData', 'alBufferf', 'alBuffer3f', -'alBufferfv', 'alBufferi', 'alBuffer3i', 'alBufferiv', 'alGetBufferf', -'alGetBuffer3f', 'alGetBufferfv', 'alGetBufferi', 'alGetBuffer3i', -'alGetBufferiv', 'alDopplerFactor', 'alDopplerVelocity', 'alSpeedOfSound', -'alDistanceModel', 'LPALENABLE', 'LPALDISABLE', 'LPALISENABLED', -'LPALGETSTRING', 'LPALGETBOOLEANV', 'LPALGETINTEGERV', 'LPALGETFLOATV', -'LPALGETDOUBLEV', 'LPALGETBOOLEAN', 'LPALGETINTEGER', 'LPALGETFLOAT', -'LPALGETDOUBLE', 'LPALGETERROR', 'LPALISEXTENSIONPRESENT', -'LPALGETPROCADDRESS', 'LPALGETENUMVALUE', 'LPALLISTENERF', 'LPALLISTENER3F', -'LPALLISTENERFV', 'LPALLISTENERI', 'LPALLISTENER3I', 'LPALLISTENERIV', -'LPALGETLISTENERF', 'LPALGETLISTENER3F', 'LPALGETLISTENERFV', -'LPALGETLISTENERI', 'LPALGETLISTENER3I', 'LPALGETLISTENERIV', -'LPALGENSOURCES', 'LPALDELETESOURCES', 'LPALISSOURCE', 'LPALSOURCEF', -'LPALSOURCE3F', 'LPALSOURCEFV', 'LPALSOURCEI', 'LPALSOURCE3I', 'LPALSOURCEIV', -'LPALGETSOURCEF', 'LPALGETSOURCE3F', 'LPALGETSOURCEFV', 'LPALGETSOURCEI', -'LPALGETSOURCE3I', 'LPALGETSOURCEIV', 'LPALSOURCEPLAYV', 'LPALSOURCESTOPV', -'LPALSOURCEREWINDV', 'LPALSOURCEPAUSEV', 'LPALSOURCEPLAY', 'LPALSOURCESTOP', -'LPALSOURCEREWIND', 'LPALSOURCEPAUSE', 'LPALSOURCEQUEUEBUFFERS', -'LPALSOURCEUNQUEUEBUFFERS', 'LPALGENBUFFERS', 'LPALDELETEBUFFERS', -'LPALISBUFFER', 'LPALBUFFERDATA', 'LPALBUFFERF', 'LPALBUFFER3F', -'LPALBUFFERFV', 'LPALBUFFERI', 'LPALBUFFER3I', 'LPALBUFFERIV', -'LPALGETBUFFERF', 'LPALGETBUFFER3F', 'LPALGETBUFFERFV', 'LPALGETBUFFERI', -'LPALGETBUFFER3I', 'LPALGETBUFFERIV', 'LPALDOPPLERFACTOR', -'LPALDOPPLERVELOCITY', 'LPALSPEEDOFSOUND', 'LPALDISTANCEMODEL'] + 'AL_ILLEGAL_COMMAND', 'ALboolean', 'ALchar', 'ALbyte', 'ALubyte', 'ALshort', + 'ALushort', 'ALint', 'ALuint', 'ALsizei', 'ALenum', 'ALfloat', 'ALdouble', + 'ALvoid', 'AL_NONE', 'AL_FALSE', 'AL_TRUE', 'AL_SOURCE_RELATIVE', + 'AL_CONE_INNER_ANGLE', 'AL_CONE_OUTER_ANGLE', 'AL_PITCH', 'AL_POSITION', + 'AL_DIRECTION', 'AL_VELOCITY', 'AL_LOOPING', 'AL_BUFFER', 'AL_GAIN', + 'AL_MIN_GAIN', 'AL_MAX_GAIN', 'AL_ORIENTATION', 'AL_SOURCE_STATE', + 'AL_INITIAL', 'AL_PLAYING', 'AL_PAUSED', 'AL_STOPPED', 'AL_BUFFERS_QUEUED', + 'AL_BUFFERS_PROCESSED', 'AL_SEC_OFFSET', 'AL_SAMPLE_OFFSET', 'AL_BYTE_OFFSET', + 'AL_SOURCE_TYPE', 'AL_STATIC', 'AL_STREAMING', 'AL_UNDETERMINED', + 'AL_FORMAT_MONO8', 'AL_FORMAT_MONO16', 'AL_FORMAT_STEREO8', + 'AL_FORMAT_STEREO16', 'AL_REFERENCE_DISTANCE', 'AL_ROLLOFF_FACTOR', + 'AL_CONE_OUTER_GAIN', 'AL_MAX_DISTANCE', 'AL_FREQUENCY', 'AL_BITS', + 'AL_CHANNELS', 'AL_SIZE', 'AL_UNUSED', 'AL_PENDING', 'AL_PROCESSED', + 'AL_NO_ERROR', 'AL_INVALID_NAME', 'AL_INVALID_ENUM', 'AL_INVALID_VALUE', + 'AL_INVALID_OPERATION', 'AL_OUT_OF_MEMORY', 'AL_VENDOR', 'AL_VERSION', + 'AL_RENDERER', 'AL_EXTENSIONS', 'AL_DOPPLER_FACTOR', 'AL_DOPPLER_VELOCITY', + 'AL_SPEED_OF_SOUND', 'AL_DISTANCE_MODEL', 'AL_INVERSE_DISTANCE', + 'AL_INVERSE_DISTANCE_CLAMPED', 'AL_LINEAR_DISTANCE', + 'AL_LINEAR_DISTANCE_CLAMPED', 'AL_EXPONENT_DISTANCE', + 'AL_EXPONENT_DISTANCE_CLAMPED', 'alEnable', 'alDisable', 'alIsEnabled', + 'alGetString', 'alGetBooleanv', 'alGetIntegerv', 'alGetFloatv', + 'alGetDoublev', 'alGetBoolean', 'alGetInteger', 'alGetFloat', 'alGetDouble', + 'alGetError', 'alIsExtensionPresent', 'alGetProcAddress', 'alGetEnumValue', + 'alListenerf', 'alListener3f', 'alListenerfv', 'alListeneri', 'alListener3i', + 'alListeneriv', 'alGetListenerf', 'alGetListener3f', 'alGetListenerfv', + 'alGetListeneri', 'alGetListener3i', 'alGetListeneriv', 'alGenSources', + 'alDeleteSources', 'alIsSource', 'alSourcef', 'alSource3f', 'alSourcefv', + 'alSourcei', 'alSource3i', 'alSourceiv', 'alGetSourcef', 'alGetSource3f', + 'alGetSourcefv', 'alGetSourcei', 'alGetSource3i', 'alGetSourceiv', + 'alSourcePlayv', 'alSourceStopv', 'alSourceRewindv', 'alSourcePausev', + 'alSourcePlay', 'alSourceStop', 'alSourceRewind', 'alSourcePause', + 'alSourceQueueBuffers', 'alSourceUnqueueBuffers', 'alGenBuffers', + 'alDeleteBuffers', 'alIsBuffer', 'alBufferData', 'alBufferf', 'alBuffer3f', + 'alBufferfv', 'alBufferi', 'alBuffer3i', 'alBufferiv', 'alGetBufferf', + 'alGetBuffer3f', 'alGetBufferfv', 'alGetBufferi', 'alGetBuffer3i', + 'alGetBufferiv', 'alDopplerFactor', 'alDopplerVelocity', 'alSpeedOfSound', + 'alDistanceModel', 'LPALENABLE', 'LPALDISABLE', 'LPALISENABLED', + 'LPALGETSTRING', 'LPALGETBOOLEANV', 'LPALGETINTEGERV', 'LPALGETFLOATV', + 'LPALGETDOUBLEV', 'LPALGETBOOLEAN', 'LPALGETINTEGER', 'LPALGETFLOAT', + 'LPALGETDOUBLE', 'LPALGETERROR', 'LPALISEXTENSIONPRESENT', + 'LPALGETPROCADDRESS', 'LPALGETENUMVALUE', 'LPALLISTENERF', 'LPALLISTENER3F', + 'LPALLISTENERFV', 'LPALLISTENERI', 'LPALLISTENER3I', 'LPALLISTENERIV', + 'LPALGETLISTENERF', 'LPALGETLISTENER3F', 'LPALGETLISTENERFV', + 'LPALGETLISTENERI', 'LPALGETLISTENER3I', 'LPALGETLISTENERIV', + 'LPALGENSOURCES', 'LPALDELETESOURCES', 'LPALISSOURCE', 'LPALSOURCEF', + 'LPALSOURCE3F', 'LPALSOURCEFV', 'LPALSOURCEI', 'LPALSOURCE3I', 'LPALSOURCEIV', + 'LPALGETSOURCEF', 'LPALGETSOURCE3F', 'LPALGETSOURCEFV', 'LPALGETSOURCEI', + 'LPALGETSOURCE3I', 'LPALGETSOURCEIV', 'LPALSOURCEPLAYV', 'LPALSOURCESTOPV', + 'LPALSOURCEREWINDV', 'LPALSOURCEPAUSEV', 'LPALSOURCEPLAY', 'LPALSOURCESTOP', + 'LPALSOURCEREWIND', 'LPALSOURCEPAUSE', 'LPALSOURCEQUEUEBUFFERS', + 'LPALSOURCEUNQUEUEBUFFERS', 'LPALGENBUFFERS', 'LPALDELETEBUFFERS', + 'LPALISBUFFER', 'LPALBUFFERDATA', 'LPALBUFFERF', 'LPALBUFFER3F', + 'LPALBUFFERFV', 'LPALBUFFERI', 'LPALBUFFER3I', 'LPALBUFFERIV', + 'LPALGETBUFFERF', 'LPALGETBUFFER3F', 'LPALGETBUFFERFV', 'LPALGETBUFFERI', + 'LPALGETBUFFER3I', 'LPALGETBUFFERIV', 'LPALDOPPLERFACTOR', + 'LPALDOPPLERVELOCITY', 'LPALSPEEDOFSOUND', 'LPALDISTANCEMODEL'] diff -Nru pyglet-1.4.10/pyglet/media/drivers/pulse/adaptation.py pyglet-1.5.14/pyglet/media/drivers/pulse/adaptation.py --- pyglet-1.4.10/pyglet/media/drivers/pulse/adaptation.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/pulse/adaptation.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,8 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import print_function -from __future__ import absolute_import import weakref @@ -41,12 +39,11 @@ from pyglet.media.events import MediaEvent from pyglet.media.exceptions import MediaException from pyglet.media.drivers.listener import AbstractListener -from pyglet.debug import debug_print +from pyglet.util import debug_print from . import lib_pulseaudio as pa -from .interface import PulseAudioContext, PulseAudioContext, PulseAudioMainLoop, PulseAudioStream +from .interface import PulseAudioContext, PulseAudioMainLoop, PulseAudioStream -import pyglet _debug = debug_print('debug_media') @@ -58,7 +55,7 @@ self.lock = self.mainloop self.context = None - self._players = pyglet.app.WeakSet() + self._players = weakref.WeakSet() self._listener = PulseAudioListener(self) def __del__(self): diff -Nru pyglet-1.4.10/pyglet/media/drivers/pulse/__init__.py pyglet-1.5.14/pyglet/media/drivers/pulse/__init__.py --- pyglet-1.4.10/pyglet/media/drivers/pulse/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/pulse/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,13 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import from .adaptation import PulseAudioDriver import pyglet _debug = pyglet.options['debug_media'] + def create_audio_driver(): driver = PulseAudioDriver() driver.connect() diff -Nru pyglet-1.4.10/pyglet/media/drivers/pulse/interface.py pyglet-1.5.14/pyglet/media/drivers/pulse/interface.py --- pyglet-1.4.10/pyglet/media/drivers/pulse/interface.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/pulse/interface.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,16 +32,13 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import print_function -from __future__ import absolute_import -import ctypes import sys import weakref from . import lib_pulseaudio as pa from pyglet.media.exceptions import MediaException -from pyglet.debug import debug_print +from pyglet.util import debug_print import pyglet _debug = debug_print('debug_media') @@ -76,18 +73,15 @@ self.message = message def __str__(self): - return '{}: [{}] {}'.format(self.__class__.__name__, - self.error_code, - self.message) + return '{}: [{}] {}'.format(self.__class__.__name__, self.error_code, self.message) __repr__ = __str__ -class PulseAudioMainLoop(object): +class PulseAudioMainLoop: def __init__(self): self._pa_threaded_mainloop = pa.pa_threaded_mainloop_new() - self._pa_mainloop = pa.pa_threaded_mainloop_get_api( - self._pa_threaded_mainloop) + self._pa_mainloop = pa.pa_threaded_mainloop_get_api(self._pa_threaded_mainloop) self._lock_count = 0 def __del__(self): @@ -166,7 +160,7 @@ self.unlock() -class PulseAudioLockable(object): +class PulseAudioLockable: def __init__(self, mainloop): assert mainloop is not None self.mainloop = weakref.ref(mainloop) diff -Nru pyglet-1.4.10/pyglet/media/drivers/pulse/lib_pulseaudio.py pyglet-1.5.14/pyglet/media/drivers/pulse/lib_pulseaudio.py --- pyglet-1.4.10/pyglet/media/drivers/pulse/lib_pulseaudio.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/pulse/lib_pulseaudio.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,7 +32,7 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Wrapper for pulse +"""Wrapper for pulse Generated with: tools/genwrappers.py pulseaudio @@ -41,10 +41,7 @@ IMPORTANT: struct_timeval is incorrectly parsed by tools/genwrappers.py and was manually edited in this file. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" import ctypes from ctypes import * @@ -63,6 +60,7 @@ if sizeof(t) == sizeof(c_size_t): c_ptrdiff_t = t + class c_void(Structure): # c_void_p is a buggy return type, converting to int, so # POINTER(None) == c_void_p is actually written as @@ -70,19 +68,18 @@ _fields_ = [('dummy', c_int)] - # /usr/include/pulse/version.h:40 pa_get_library_version = _lib.pa_get_library_version pa_get_library_version.restype = c_char_p pa_get_library_version.argtypes = [] -PA_API_VERSION = 12 # /usr/include/pulse/version.h:46 -PA_PROTOCOL_VERSION = 30 # /usr/include/pulse/version.h:50 -PA_MAJOR = 6 # /usr/include/pulse/version.h:53 -PA_MINOR = 0 # /usr/include/pulse/version.h:56 -PA_MICRO = 0 # /usr/include/pulse/version.h:59 -PA_CHANNELS_MAX = 32 # /usr/include/pulse/sample.h:128 -PA_RATE_MAX = 192000 # /usr/include/pulse/sample.h:131 +PA_API_VERSION = 12 # /usr/include/pulse/version.h:46 +PA_PROTOCOL_VERSION = 30 # /usr/include/pulse/version.h:50 +PA_MAJOR = 6 # /usr/include/pulse/version.h:53 +PA_MINOR = 0 # /usr/include/pulse/version.h:56 +PA_MICRO = 0 # /usr/include/pulse/version.h:59 +PA_CHANNELS_MAX = 32 # /usr/include/pulse/sample.h:128 +PA_RATE_MAX = 192000 # /usr/include/pulse/sample.h:131 enum_pa_sample_format = c_int PA_SAMPLE_U8 = 0 PA_SAMPLE_ALAW = 1 @@ -99,21 +96,25 @@ PA_SAMPLE_S24_32BE = 12 PA_SAMPLE_MAX = 13 PA_SAMPLE_INVALID = -1 -pa_sample_format_t = enum_pa_sample_format # /usr/include/pulse/sample.h:179 +pa_sample_format_t = enum_pa_sample_format # /usr/include/pulse/sample.h:179 + + class struct_pa_sample_spec(Structure): __slots__ = [ 'format', 'rate', 'channels', ] + + struct_pa_sample_spec._fields_ = [ ('format', pa_sample_format_t), ('rate', c_uint32), ('channels', c_uint8), ] -pa_sample_spec = struct_pa_sample_spec # /usr/include/pulse/sample.h:257 -pa_usec_t = c_uint64 # /usr/include/pulse/sample.h:260 +pa_sample_spec = struct_pa_sample_spec # /usr/include/pulse/sample.h:257 +pa_usec_t = c_uint64 # /usr/include/pulse/sample.h:260 # /usr/include/pulse/sample.h:263 pa_bytes_per_second = _lib.pa_bytes_per_second pa_bytes_per_second.restype = c_size_t @@ -150,19 +151,19 @@ pa_sample_spec_init.argtypes = [POINTER(pa_sample_spec)] # /usr/include/pulse/sample.h:291 -#pa_sample_format_valid = _lib.pa_sample_format_valid -#pa_sample_format_valid.restype = c_int -#pa_sample_format_valid.argtypes = [c_uint] +# pa_sample_format_valid = _lib.pa_sample_format_valid +# pa_sample_format_valid.restype = c_int +# pa_sample_format_valid.argtypes = [c_uint] # /usr/include/pulse/sample.h:294 -#pa_sample_rate_valid = _lib.pa_sample_rate_valid -#pa_sample_rate_valid.restype = c_int -#pa_sample_rate_valid.argtypes = [c_uint32] +# pa_sample_rate_valid = _lib.pa_sample_rate_valid +# pa_sample_rate_valid.restype = c_int +# pa_sample_rate_valid.argtypes = [c_uint32] # /usr/include/pulse/sample.h:298 -#pa_channels_valid = _lib.pa_channels_valid -#pa_channels_valid.restype = c_int -#pa_channels_valid.argtypes = [c_uint8] +# pa_channels_valid = _lib.pa_channels_valid +# pa_channels_valid.restype = c_int +# pa_channels_valid.argtypes = [c_uint8] # /usr/include/pulse/sample.h:301 pa_sample_spec_valid = _lib.pa_sample_spec_valid @@ -184,13 +185,13 @@ pa_parse_sample_format.restype = pa_sample_format_t pa_parse_sample_format.argtypes = [c_char_p] -PA_SAMPLE_SPEC_SNPRINT_MAX = 32 # /usr/include/pulse/sample.h:317 +PA_SAMPLE_SPEC_SNPRINT_MAX = 32 # /usr/include/pulse/sample.h:317 # /usr/include/pulse/sample.h:320 pa_sample_spec_snprint = _lib.pa_sample_spec_snprint pa_sample_spec_snprint.restype = c_char_p pa_sample_spec_snprint.argtypes = [c_char_p, c_size_t, POINTER(pa_sample_spec)] -PA_BYTES_SNPRINT_MAX = 11 # /usr/include/pulse/sample.h:327 +PA_BYTES_SNPRINT_MAX = 11 # /usr/include/pulse/sample.h:327 # /usr/include/pulse/sample.h:330 pa_bytes_snprint = _lib.pa_bytes_snprint pa_bytes_snprint.restype = c_char_p @@ -214,38 +215,38 @@ PA_CONTEXT_READY = 4 PA_CONTEXT_FAILED = 5 PA_CONTEXT_TERMINATED = 6 -pa_context_state_t = enum_pa_context_state # /usr/include/pulse/def.h:45 +pa_context_state_t = enum_pa_context_state # /usr/include/pulse/def.h:45 enum_pa_stream_state = c_int PA_STREAM_UNCONNECTED = 0 PA_STREAM_CREATING = 1 PA_STREAM_READY = 2 PA_STREAM_FAILED = 3 PA_STREAM_TERMINATED = 4 -pa_stream_state_t = enum_pa_stream_state # /usr/include/pulse/def.h:74 +pa_stream_state_t = enum_pa_stream_state # /usr/include/pulse/def.h:74 enum_pa_operation_state = c_int PA_OPERATION_RUNNING = 0 PA_OPERATION_DONE = 1 PA_OPERATION_CANCELLED = 2 -pa_operation_state_t = enum_pa_operation_state # /usr/include/pulse/def.h:102 +pa_operation_state_t = enum_pa_operation_state # /usr/include/pulse/def.h:102 enum_pa_context_flags = c_int PA_CONTEXT_NOFLAGS = 0 PA_CONTEXT_NOAUTOSPAWN = 1 PA_CONTEXT_NOFAIL = 2 -pa_context_flags_t = enum_pa_context_flags # /usr/include/pulse/def.h:122 +pa_context_flags_t = enum_pa_context_flags # /usr/include/pulse/def.h:122 enum_pa_direction = c_int PA_DIRECTION_OUTPUT = 1 PA_DIRECTION_INPUT = 2 -pa_direction_t = enum_pa_direction # /usr/include/pulse/def.h:137 +pa_direction_t = enum_pa_direction # /usr/include/pulse/def.h:137 enum_pa_device_type = c_int PA_DEVICE_TYPE_SINK = 0 PA_DEVICE_TYPE_SOURCE = 1 -pa_device_type_t = enum_pa_device_type # /usr/include/pulse/def.h:148 +pa_device_type_t = enum_pa_device_type # /usr/include/pulse/def.h:148 enum_pa_stream_direction = c_int PA_STREAM_NODIRECTION = 0 PA_STREAM_PLAYBACK = 1 PA_STREAM_RECORD = 2 PA_STREAM_UPLOAD = 3 -pa_stream_direction_t = enum_pa_stream_direction # /usr/include/pulse/def.h:161 +pa_stream_direction_t = enum_pa_stream_direction # /usr/include/pulse/def.h:161 enum_pa_stream_flags = c_int PA_STREAM_NOFLAGS = 0 PA_STREAM_START_CORKED = 1 @@ -268,7 +269,9 @@ PA_STREAM_FAIL_ON_SUSPEND = 131072 PA_STREAM_RELATIVE_VOLUME = 262144 PA_STREAM_PASSTHROUGH = 524288 -pa_stream_flags_t = enum_pa_stream_flags # /usr/include/pulse/def.h:355 +pa_stream_flags_t = enum_pa_stream_flags # /usr/include/pulse/def.h:355 + + class struct_pa_buffer_attr(Structure): __slots__ = [ 'maxlength', @@ -277,6 +280,8 @@ 'minreq', 'fragsize', ] + + struct_pa_buffer_attr._fields_ = [ ('maxlength', c_uint32), ('tlength', c_uint32), @@ -285,7 +290,7 @@ ('fragsize', c_uint32), ] -pa_buffer_attr = struct_pa_buffer_attr # /usr/include/pulse/def.h:452 +pa_buffer_attr = struct_pa_buffer_attr # /usr/include/pulse/def.h:452 enum_pa_error_code = c_int PA_OK = 0 PA_ERR_ACCESS = 1 @@ -315,7 +320,7 @@ PA_ERR_IO = 25 PA_ERR_BUSY = 26 PA_ERR_MAX = 27 -pa_error_code_t = enum_pa_error_code # /usr/include/pulse/def.h:484 +pa_error_code_t = enum_pa_error_code # /usr/include/pulse/def.h:484 enum_pa_subscription_mask = c_int PA_SUBSCRIPTION_MASK_NULL = 0 PA_SUBSCRIPTION_MASK_SINK = 1 @@ -329,7 +334,7 @@ PA_SUBSCRIPTION_MASK_AUTOLOAD = 256 PA_SUBSCRIPTION_MASK_CARD = 512 PA_SUBSCRIPTION_MASK_ALL = 767 -pa_subscription_mask_t = enum_pa_subscription_mask # /usr/include/pulse/def.h:554 +pa_subscription_mask_t = enum_pa_subscription_mask # /usr/include/pulse/def.h:554 enum_pa_subscription_event_type = c_int PA_SUBSCRIPTION_EVENT_SINK = 0 PA_SUBSCRIPTION_EVENT_SOURCE = 1 @@ -346,7 +351,9 @@ PA_SUBSCRIPTION_EVENT_CHANGE = 16 PA_SUBSCRIPTION_EVENT_REMOVE = 32 PA_SUBSCRIPTION_EVENT_TYPE_MASK = 48 -pa_subscription_event_type_t = enum_pa_subscription_event_type # /usr/include/pulse/def.h:605 +pa_subscription_event_type_t = enum_pa_subscription_event_type # /usr/include/pulse/def.h:605 + + class struct_pa_timing_info(Structure): __slots__ = [ 'timestamp', @@ -364,10 +371,12 @@ 'since_underrun', ] + class struct_timeval(Structure): _fields_ = [("tv_sec", c_long), ("tv_usec", c_long)] + struct_pa_timing_info._fields_ = [ ('timestamp', struct_timeval), ('synchronized_clocks', c_int), @@ -384,26 +393,30 @@ ('since_underrun', c_int64), ] -pa_timing_info = struct_pa_timing_info # /usr/include/pulse/def.h:725 +pa_timing_info = struct_pa_timing_info # /usr/include/pulse/def.h:725 + + class struct_pa_spawn_api(Structure): __slots__ = [ 'prefork', 'postfork', 'atfork', ] + + struct_pa_spawn_api._fields_ = [ ('prefork', POINTER(CFUNCTYPE(None))), ('postfork', POINTER(CFUNCTYPE(None))), ('atfork', POINTER(CFUNCTYPE(None))), ] -pa_spawn_api = struct_pa_spawn_api # /usr/include/pulse/def.h:749 +pa_spawn_api = struct_pa_spawn_api # /usr/include/pulse/def.h:749 enum_pa_seek_mode = c_int PA_SEEK_RELATIVE = 0 PA_SEEK_ABSOLUTE = 1 PA_SEEK_RELATIVE_ON_READ = 2 PA_SEEK_RELATIVE_END = 3 -pa_seek_mode_t = enum_pa_seek_mode # /usr/include/pulse/def.h:764 +pa_seek_mode_t = enum_pa_seek_mode # /usr/include/pulse/def.h:764 enum_pa_sink_flags = c_int PA_SINK_NOFLAGS = 0 PA_SINK_HW_VOLUME_CTRL = 1 @@ -415,7 +428,7 @@ PA_SINK_FLAT_VOLUME = 64 PA_SINK_DYNAMIC_LATENCY = 128 PA_SINK_SET_FORMATS = 256 -pa_sink_flags_t = enum_pa_sink_flags # /usr/include/pulse/def.h:829 +pa_sink_flags_t = enum_pa_sink_flags # /usr/include/pulse/def.h:829 enum_pa_sink_state = c_int PA_SINK_INVALID_STATE = -1 PA_SINK_RUNNING = 0 @@ -423,7 +436,7 @@ PA_SINK_SUSPENDED = 2 PA_SINK_INIT = -2 PA_SINK_UNLINKED = -3 -pa_sink_state_t = enum_pa_sink_state # /usr/include/pulse/def.h:875 +pa_sink_state_t = enum_pa_sink_state # /usr/include/pulse/def.h:875 enum_pa_source_flags = c_int PA_SOURCE_NOFLAGS = 0 PA_SOURCE_HW_VOLUME_CTRL = 1 @@ -434,7 +447,7 @@ PA_SOURCE_DECIBEL_VOLUME = 32 PA_SOURCE_DYNAMIC_LATENCY = 64 PA_SOURCE_FLAT_VOLUME = 128 -pa_source_flags_t = enum_pa_source_flags # /usr/include/pulse/def.h:946 +pa_source_flags_t = enum_pa_source_flags # /usr/include/pulse/def.h:946 enum_pa_source_state = c_int PA_SOURCE_INVALID_STATE = -1 PA_SOURCE_RUNNING = 0 @@ -442,91 +455,126 @@ PA_SOURCE_SUSPENDED = 2 PA_SOURCE_INIT = -2 PA_SOURCE_UNLINKED = -3 -pa_source_state_t = enum_pa_source_state # /usr/include/pulse/def.h:991 -pa_free_cb_t = CFUNCTYPE(None, POINTER(None)) # /usr/include/pulse/def.h:1014 +pa_source_state_t = enum_pa_source_state # /usr/include/pulse/def.h:991 +pa_free_cb_t = CFUNCTYPE(None, POINTER(None)) # /usr/include/pulse/def.h:1014 enum_pa_port_available = c_int PA_PORT_AVAILABLE_UNKNOWN = 0 PA_PORT_AVAILABLE_NO = 1 PA_PORT_AVAILABLE_YES = 2 -pa_port_available_t = enum_pa_port_available # /usr/include/pulse/def.h:1040 +pa_port_available_t = enum_pa_port_available # /usr/include/pulse/def.h:1040 + + class struct_pa_mainloop_api(Structure): __slots__ = [ ] + + struct_pa_mainloop_api._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_mainloop_api(Structure): __slots__ = [ ] + + struct_pa_mainloop_api._fields_ = [ ('_opaque_struct', c_int) ] -pa_mainloop_api = struct_pa_mainloop_api # /usr/include/pulse/mainloop-api.h:47 +pa_mainloop_api = struct_pa_mainloop_api # /usr/include/pulse/mainloop-api.h:47 enum_pa_io_event_flags = c_int PA_IO_EVENT_NULL = 0 PA_IO_EVENT_INPUT = 1 PA_IO_EVENT_OUTPUT = 2 PA_IO_EVENT_HANGUP = 4 PA_IO_EVENT_ERROR = 8 -pa_io_event_flags_t = enum_pa_io_event_flags # /usr/include/pulse/mainloop-api.h:56 +pa_io_event_flags_t = enum_pa_io_event_flags # /usr/include/pulse/mainloop-api.h:56 + + class struct_pa_io_event(Structure): __slots__ = [ ] + + struct_pa_io_event._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_io_event(Structure): __slots__ = [ ] + + struct_pa_io_event._fields_ = [ ('_opaque_struct', c_int) ] -pa_io_event = struct_pa_io_event # /usr/include/pulse/mainloop-api.h:59 -pa_io_event_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_io_event), c_int, pa_io_event_flags_t, POINTER(None)) # /usr/include/pulse/mainloop-api.h:61 -pa_io_event_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_io_event), POINTER(None)) # /usr/include/pulse/mainloop-api.h:63 +pa_io_event = struct_pa_io_event # /usr/include/pulse/mainloop-api.h:59 +pa_io_event_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_io_event), c_int, pa_io_event_flags_t, + POINTER(None)) # /usr/include/pulse/mainloop-api.h:61 +pa_io_event_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_io_event), + POINTER(None)) # /usr/include/pulse/mainloop-api.h:63 + + class struct_pa_time_event(Structure): __slots__ = [ ] + + struct_pa_time_event._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_time_event(Structure): __slots__ = [ ] + + struct_pa_time_event._fields_ = [ ('_opaque_struct', c_int) ] -pa_time_event = struct_pa_time_event # /usr/include/pulse/mainloop-api.h:66 +pa_time_event = struct_pa_time_event # /usr/include/pulse/mainloop-api.h:66 + +pa_time_event_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_time_event), POINTER(struct_timeval), + POINTER(None)) # /usr/include/pulse/mainloop-api.h:68 +pa_time_event_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_time_event), + POINTER(None)) # /usr/include/pulse/mainloop-api.h:70 + -pa_time_event_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_time_event), POINTER(struct_timeval), POINTER(None)) # /usr/include/pulse/mainloop-api.h:68 -pa_time_event_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_time_event), POINTER(None)) # /usr/include/pulse/mainloop-api.h:70 class struct_pa_defer_event(Structure): __slots__ = [ ] + + struct_pa_defer_event._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_defer_event(Structure): __slots__ = [ ] + + struct_pa_defer_event._fields_ = [ ('_opaque_struct', c_int) ] -pa_defer_event = struct_pa_defer_event # /usr/include/pulse/mainloop-api.h:73 -pa_defer_event_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_defer_event), POINTER(None)) # /usr/include/pulse/mainloop-api.h:75 -pa_defer_event_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_defer_event), POINTER(None)) # /usr/include/pulse/mainloop-api.h:77 +pa_defer_event = struct_pa_defer_event # /usr/include/pulse/mainloop-api.h:73 +pa_defer_event_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_defer_event), + POINTER(None)) # /usr/include/pulse/mainloop-api.h:75 +pa_defer_event_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_defer_event), + POINTER(None)) # /usr/include/pulse/mainloop-api.h:77 # /usr/include/pulse/mainloop-api.h:120 pa_mainloop_api_once = _lib.pa_mainloop_api_once pa_mainloop_api_once.restype = None -pa_mainloop_api_once.argtypes = [POINTER(pa_mainloop_api), CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(None)), POINTER(None)] +pa_mainloop_api_once.argtypes = [POINTER(pa_mainloop_api), CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(None)), + POINTER(None)] enum_pa_channel_position = c_int PA_CHANNEL_POSITION_INVALID = -1 @@ -586,8 +634,8 @@ PA_CHANNEL_POSITION_TOP_REAR_RIGHT = 42 PA_CHANNEL_POSITION_TOP_REAR_CENTER = 43 PA_CHANNEL_POSITION_MAX = 44 -pa_channel_position_t = enum_pa_channel_position # /usr/include/pulse/channelmap.h:147 -pa_channel_position_mask_t = c_uint64 # /usr/include/pulse/channelmap.h:210 +pa_channel_position_t = enum_pa_channel_position # /usr/include/pulse/channelmap.h:147 +pa_channel_position_mask_t = c_uint64 # /usr/include/pulse/channelmap.h:210 enum_pa_channel_map_def = c_int PA_CHANNEL_MAP_AIFF = 0 PA_CHANNEL_MAP_ALSA = 1 @@ -596,18 +644,22 @@ PA_CHANNEL_MAP_OSS = 4 PA_CHANNEL_MAP_DEF_MAX = 5 PA_CHANNEL_MAP_DEFAULT = 0 -pa_channel_map_def_t = enum_pa_channel_map_def # /usr/include/pulse/channelmap.h:247 +pa_channel_map_def_t = enum_pa_channel_map_def # /usr/include/pulse/channelmap.h:247 + + class struct_pa_channel_map(Structure): __slots__ = [ 'channels', 'map', ] + + struct_pa_channel_map._fields_ = [ ('channels', c_uint8), ('map', pa_channel_position_t * 32), ] -pa_channel_map = struct_pa_channel_map # /usr/include/pulse/channelmap.h:268 +pa_channel_map = struct_pa_channel_map # /usr/include/pulse/channelmap.h:268 # /usr/include/pulse/channelmap.h:273 pa_channel_map_init = _lib.pa_channel_map_init pa_channel_map_init.restype = POINTER(pa_channel_map) @@ -648,7 +700,7 @@ pa_channel_position_to_pretty_string.restype = c_char_p pa_channel_position_to_pretty_string.argtypes = [pa_channel_position_t] -PA_CHANNEL_MAP_SNPRINT_MAX = 336 # /usr/include/pulse/channelmap.h:307 +PA_CHANNEL_MAP_SNPRINT_MAX = 336 # /usr/include/pulse/channelmap.h:307 # /usr/include/pulse/channelmap.h:310 pa_channel_map_snprint = _lib.pa_channel_map_snprint pa_channel_map_snprint.restype = c_char_p @@ -709,22 +761,28 @@ pa_channel_map_mask.restype = pa_channel_position_mask_t pa_channel_map_mask.argtypes = [POINTER(pa_channel_map)] + class struct_pa_operation(Structure): __slots__ = [ ] + + struct_pa_operation._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_operation(Structure): __slots__ = [ ] + + struct_pa_operation._fields_ = [ ('_opaque_struct', c_int) ] -pa_operation = struct_pa_operation # /usr/include/pulse/operation.h:33 -pa_operation_notify_cb_t = CFUNCTYPE(None, POINTER(pa_operation), POINTER(None)) # /usr/include/pulse/operation.h:36 +pa_operation = struct_pa_operation # /usr/include/pulse/operation.h:33 +pa_operation_notify_cb_t = CFUNCTYPE(None, POINTER(pa_operation), POINTER(None)) # /usr/include/pulse/operation.h:36 # /usr/include/pulse/operation.h:39 pa_operation_ref = _lib.pa_operation_ref pa_operation_ref.restype = POINTER(pa_operation) @@ -750,39 +808,53 @@ pa_operation_set_state_callback.restype = None pa_operation_set_state_callback.argtypes = [POINTER(pa_operation), pa_operation_notify_cb_t, POINTER(None)] + class struct_pa_context(Structure): __slots__ = [ ] + + struct_pa_context._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_context(Structure): __slots__ = [ ] + + struct_pa_context._fields_ = [ ('_opaque_struct', c_int) ] -pa_context = struct_pa_context # /usr/include/pulse/context.h:154 -pa_context_notify_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(None)) # /usr/include/pulse/context.h:157 -pa_context_success_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_int, POINTER(None)) # /usr/include/pulse/context.h:160 +pa_context = struct_pa_context # /usr/include/pulse/context.h:154 +pa_context_notify_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(None)) # /usr/include/pulse/context.h:157 +pa_context_success_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_int, POINTER(None)) # /usr/include/pulse/context.h:160 + + class struct_pa_proplist(Structure): __slots__ = [ ] + + struct_pa_proplist._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_proplist(Structure): __slots__ = [ ] + + struct_pa_proplist._fields_ = [ ('_opaque_struct', c_int) ] -pa_proplist = struct_pa_proplist # /usr/include/pulse/proplist.h:272 -pa_context_event_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_char_p, POINTER(pa_proplist), POINTER(None)) # /usr/include/pulse/context.h:167 +pa_proplist = struct_pa_proplist # /usr/include/pulse/proplist.h:272 +pa_context_event_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_char_p, POINTER(pa_proplist), + POINTER(None)) # /usr/include/pulse/context.h:167 # /usr/include/pulse/context.h:172 pa_context_new = _lib.pa_context_new pa_context_new.restype = POINTER(pa_context) @@ -887,11 +959,12 @@ PA_UPDATE_SET = 0 PA_UPDATE_MERGE = 1 PA_UPDATE_REPLACE = 2 -pa_update_mode_t = enum_pa_update_mode # /usr/include/pulse/proplist.h:337 +pa_update_mode_t = enum_pa_update_mode # /usr/include/pulse/proplist.h:337 # /usr/include/pulse/context.h:248 pa_context_proplist_update = _lib.pa_context_proplist_update pa_context_proplist_update.restype = POINTER(pa_operation) -pa_context_proplist_update.argtypes = [POINTER(pa_context), pa_update_mode_t, POINTER(pa_proplist), pa_context_success_cb_t, POINTER(None)] +pa_context_proplist_update.argtypes = [POINTER(pa_context), pa_update_mode_t, POINTER(pa_proplist), + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/context.h:251 pa_context_proplist_remove = _lib.pa_context_proplist_remove @@ -919,22 +992,26 @@ pa_context_get_tile_size.argtypes = [POINTER(pa_context), POINTER(pa_sample_spec)] # /usr/include/pulse/context.h:287 -#pa_context_load_cookie_from_file = _lib.pa_context_load_cookie_from_file -#pa_context_load_cookie_from_file.restype = c_int -#pa_context_load_cookie_from_file.argtypes = [POINTER(pa_context), c_char_p] +# pa_context_load_cookie_from_file = _lib.pa_context_load_cookie_from_file +# pa_context_load_cookie_from_file.restype = c_int +# pa_context_load_cookie_from_file.argtypes = [POINTER(pa_context), c_char_p] + +pa_volume_t = c_uint32 # /usr/include/pulse/volume.h:120 + -pa_volume_t = c_uint32 # /usr/include/pulse/volume.h:120 class struct_pa_cvolume(Structure): __slots__ = [ 'channels', 'values', ] + + struct_pa_cvolume._fields_ = [ ('channels', c_uint8), ('values', pa_volume_t * 32), ] -pa_cvolume = struct_pa_cvolume # /usr/include/pulse/volume.h:151 +pa_cvolume = struct_pa_cvolume # /usr/include/pulse/volume.h:151 # /usr/include/pulse/volume.h:154 pa_cvolume_equal = _lib.pa_cvolume_equal pa_cvolume_equal.restype = c_int @@ -950,41 +1027,41 @@ pa_cvolume_set.restype = POINTER(pa_cvolume) pa_cvolume_set.argtypes = [POINTER(pa_cvolume), c_uint, pa_volume_t] -PA_CVOLUME_SNPRINT_MAX = 320 # /usr/include/pulse/volume.h:175 +PA_CVOLUME_SNPRINT_MAX = 320 # /usr/include/pulse/volume.h:175 # /usr/include/pulse/volume.h:178 pa_cvolume_snprint = _lib.pa_cvolume_snprint pa_cvolume_snprint.restype = c_char_p pa_cvolume_snprint.argtypes = [c_char_p, c_size_t, POINTER(pa_cvolume)] -PA_SW_CVOLUME_SNPRINT_DB_MAX = 448 # /usr/include/pulse/volume.h:185 +PA_SW_CVOLUME_SNPRINT_DB_MAX = 448 # /usr/include/pulse/volume.h:185 # /usr/include/pulse/volume.h:188 pa_sw_cvolume_snprint_dB = _lib.pa_sw_cvolume_snprint_dB pa_sw_cvolume_snprint_dB.restype = c_char_p pa_sw_cvolume_snprint_dB.argtypes = [c_char_p, c_size_t, POINTER(pa_cvolume)] -PA_CVOLUME_SNPRINT_VERBOSE_MAX = 1984 # /usr/include/pulse/volume.h:194 +PA_CVOLUME_SNPRINT_VERBOSE_MAX = 1984 # /usr/include/pulse/volume.h:194 # /usr/include/pulse/volume.h:200 -#pa_cvolume_snprint_verbose = _lib.pa_cvolume_snprint_verbose -#pa_cvolume_snprint_verbose.restype = c_char_p -#pa_cvolume_snprint_verbose.argtypes = [c_char_p, c_size_t, POINTER(pa_cvolume), POINTER(pa_channel_map), c_int] +# pa_cvolume_snprint_verbose = _lib.pa_cvolume_snprint_verbose +# pa_cvolume_snprint_verbose.restype = c_char_p +# pa_cvolume_snprint_verbose.argtypes = [c_char_p, c_size_t, POINTER(pa_cvolume), POINTER(pa_channel_map), c_int] -PA_VOLUME_SNPRINT_MAX = 10 # /usr/include/pulse/volume.h:207 +PA_VOLUME_SNPRINT_MAX = 10 # /usr/include/pulse/volume.h:207 # /usr/include/pulse/volume.h:210 pa_volume_snprint = _lib.pa_volume_snprint pa_volume_snprint.restype = c_char_p pa_volume_snprint.argtypes = [c_char_p, c_size_t, pa_volume_t] -PA_SW_VOLUME_SNPRINT_DB_MAX = 11 # /usr/include/pulse/volume.h:217 +PA_SW_VOLUME_SNPRINT_DB_MAX = 11 # /usr/include/pulse/volume.h:217 # /usr/include/pulse/volume.h:220 pa_sw_volume_snprint_dB = _lib.pa_sw_volume_snprint_dB pa_sw_volume_snprint_dB.restype = c_char_p pa_sw_volume_snprint_dB.argtypes = [c_char_p, c_size_t, pa_volume_t] -PA_VOLUME_SNPRINT_VERBOSE_MAX = 35 # /usr/include/pulse/volume.h:226 +PA_VOLUME_SNPRINT_VERBOSE_MAX = 35 # /usr/include/pulse/volume.h:226 # /usr/include/pulse/volume.h:231 -#pa_volume_snprint_verbose = _lib.pa_volume_snprint_verbose -#pa_volume_snprint_verbose.restype = c_char_p -#pa_volume_snprint_verbose.argtypes = [c_char_p, c_size_t, pa_volume_t, c_int] +# pa_volume_snprint_verbose = _lib.pa_volume_snprint_verbose +# pa_volume_snprint_verbose.restype = c_char_p +# pa_volume_snprint_verbose.argtypes = [c_char_p, c_size_t, pa_volume_t, c_int] # /usr/include/pulse/volume.h:234 pa_cvolume_avg = _lib.pa_cvolume_avg @@ -1151,25 +1228,32 @@ pa_cvolume_dec.restype = POINTER(pa_cvolume) pa_cvolume_dec.argtypes = [POINTER(pa_cvolume), pa_volume_t] + class struct_pa_stream(Structure): __slots__ = [ ] + + struct_pa_stream._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_stream(Structure): __slots__ = [ ] + + struct_pa_stream._fields_ = [ ('_opaque_struct', c_int) ] -pa_stream = struct_pa_stream # /usr/include/pulse/stream.h:335 -pa_stream_success_cb_t = CFUNCTYPE(None, POINTER(pa_stream), c_int, POINTER(None)) # /usr/include/pulse/stream.h:338 -pa_stream_request_cb_t = CFUNCTYPE(None, POINTER(pa_stream), c_size_t, POINTER(None)) # /usr/include/pulse/stream.h:341 -pa_stream_notify_cb_t = CFUNCTYPE(None, POINTER(pa_stream), POINTER(None)) # /usr/include/pulse/stream.h:344 -pa_stream_event_cb_t = CFUNCTYPE(None, POINTER(pa_stream), c_char_p, POINTER(pa_proplist), POINTER(None)) # /usr/include/pulse/stream.h:352 +pa_stream = struct_pa_stream # /usr/include/pulse/stream.h:335 +pa_stream_success_cb_t = CFUNCTYPE(None, POINTER(pa_stream), c_int, POINTER(None)) # /usr/include/pulse/stream.h:338 +pa_stream_request_cb_t = CFUNCTYPE(None, POINTER(pa_stream), c_size_t, POINTER(None)) # /usr/include/pulse/stream.h:341 +pa_stream_notify_cb_t = CFUNCTYPE(None, POINTER(pa_stream), POINTER(None)) # /usr/include/pulse/stream.h:344 +pa_stream_event_cb_t = CFUNCTYPE(None, POINTER(pa_stream), c_char_p, POINTER(pa_proplist), + POINTER(None)) # /usr/include/pulse/stream.h:352 # /usr/include/pulse/stream.h:357 pa_stream_new = _lib.pa_stream_new pa_stream_new.restype = POINTER(pa_stream) @@ -1178,13 +1262,17 @@ # /usr/include/pulse/stream.h:366 pa_stream_new_with_proplist = _lib.pa_stream_new_with_proplist pa_stream_new_with_proplist.restype = POINTER(pa_stream) -pa_stream_new_with_proplist.argtypes = [POINTER(pa_context), c_char_p, POINTER(pa_sample_spec), POINTER(pa_channel_map), POINTER(pa_proplist)] +pa_stream_new_with_proplist.argtypes = [POINTER(pa_context), c_char_p, POINTER(pa_sample_spec), POINTER(pa_channel_map), + POINTER(pa_proplist)] + class struct_pa_format_info(Structure): __slots__ = [ 'encoding', 'plist', ] + + enum_pa_encoding = c_int PA_ENCODING_ANY = 0 PA_ENCODING_PCM = 1 @@ -1195,17 +1283,18 @@ PA_ENCODING_MPEG2_AAC_IEC61937 = 6 PA_ENCODING_MAX = 7 PA_ENCODING_INVALID = -1 -pa_encoding_t = enum_pa_encoding # /usr/include/pulse/format.h:64 +pa_encoding_t = enum_pa_encoding # /usr/include/pulse/format.h:64 struct_pa_format_info._fields_ = [ ('encoding', pa_encoding_t), ('plist', POINTER(pa_proplist)), ] -pa_format_info = struct_pa_format_info # /usr/include/pulse/format.h:91 +pa_format_info = struct_pa_format_info # /usr/include/pulse/format.h:91 # /usr/include/pulse/stream.h:377 pa_stream_new_extended = _lib.pa_stream_new_extended pa_stream_new_extended.restype = POINTER(pa_stream) -pa_stream_new_extended.argtypes = [POINTER(pa_context), c_char_p, POINTER(POINTER(pa_format_info)), c_uint, POINTER(pa_proplist)] +pa_stream_new_extended.argtypes = [POINTER(pa_context), c_char_p, POINTER(POINTER(pa_format_info)), c_uint, + POINTER(pa_proplist)] # /usr/include/pulse/stream.h:385 pa_stream_unref = _lib.pa_stream_unref @@ -1255,7 +1344,8 @@ # /usr/include/pulse/stream.h:458 pa_stream_connect_playback = _lib.pa_stream_connect_playback pa_stream_connect_playback.restype = c_int -pa_stream_connect_playback.argtypes = [POINTER(pa_stream), c_char_p, POINTER(pa_buffer_attr), pa_stream_flags_t, POINTER(pa_cvolume), POINTER(pa_stream)] +pa_stream_connect_playback.argtypes = [POINTER(pa_stream), c_char_p, POINTER(pa_buffer_attr), pa_stream_flags_t, + POINTER(pa_cvolume), POINTER(pa_stream)] # /usr/include/pulse/stream.h:467 pa_stream_connect_record = _lib.pa_stream_connect_record @@ -1283,9 +1373,9 @@ pa_stream_write.argtypes = [POINTER(pa_stream), POINTER(None), c_size_t, pa_free_cb_t, c_int64, pa_seek_mode_t] # /usr/include/pulse/stream.h:557 -#pa_stream_write_ext_free = _lib.pa_stream_write_ext_free -#pa_stream_write_ext_free.restype = c_int -#pa_stream_write_ext_free.argtypes = [POINTER(pa_stream), POINTER(None), c_size_t, pa_free_cb_t, POINTER(None), c_int64, pa_seek_mode_t] +# pa_stream_write_ext_free = _lib.pa_stream_write_ext_free +# pa_stream_write_ext_free.restype = c_int +# pa_stream_write_ext_free.argtypes = [POINTER(pa_stream), POINTER(None), c_size_t, pa_free_cb_t, POINTER(None), c_int64, pa_seek_mode_t] # /usr/include/pulse/stream.h:582 pa_stream_peek = _lib.pa_stream_peek @@ -1440,7 +1530,8 @@ # /usr/include/pulse/stream.h:790 pa_stream_set_buffer_attr = _lib.pa_stream_set_buffer_attr pa_stream_set_buffer_attr.restype = POINTER(pa_operation) -pa_stream_set_buffer_attr.argtypes = [POINTER(pa_stream), POINTER(pa_buffer_attr), pa_stream_success_cb_t, POINTER(None)] +pa_stream_set_buffer_attr.argtypes = [POINTER(pa_stream), POINTER(pa_buffer_attr), pa_stream_success_cb_t, + POINTER(None)] # /usr/include/pulse/stream.h:797 pa_stream_update_sample_rate = _lib.pa_stream_update_sample_rate @@ -1450,7 +1541,8 @@ # /usr/include/pulse/stream.h:805 pa_stream_proplist_update = _lib.pa_stream_proplist_update pa_stream_proplist_update.restype = POINTER(pa_operation) -pa_stream_proplist_update.argtypes = [POINTER(pa_stream), pa_update_mode_t, POINTER(pa_proplist), pa_stream_success_cb_t, POINTER(None)] +pa_stream_proplist_update.argtypes = [POINTER(pa_stream), pa_update_mode_t, POINTER(pa_proplist), + pa_stream_success_cb_t, POINTER(None)] # /usr/include/pulse/stream.h:809 pa_stream_proplist_remove = _lib.pa_stream_proplist_remove @@ -1467,6 +1559,7 @@ pa_stream_get_monitor_stream.restype = c_uint32 pa_stream_get_monitor_stream.argtypes = [POINTER(pa_stream)] + class struct_pa_sink_port_info(Structure): __slots__ = [ 'name', @@ -1474,6 +1567,8 @@ 'priority', 'available', ] + + struct_pa_sink_port_info._fields_ = [ ('name', c_char_p), ('description', c_char_p), @@ -1481,7 +1576,9 @@ ('available', c_int), ] -pa_sink_port_info = struct_pa_sink_port_info # /usr/include/pulse/introspect.h:232 +pa_sink_port_info = struct_pa_sink_port_info # /usr/include/pulse/introspect.h:232 + + class struct_pa_sink_info(Structure): __slots__ = [ 'name', @@ -1509,6 +1606,8 @@ 'n_formats', 'formats', ] + + struct_pa_sink_info._fields_ = [ ('name', c_char_p), ('index', c_uint32), @@ -1536,8 +1635,9 @@ ('formats', POINTER(POINTER(pa_format_info))), ] -pa_sink_info = struct_pa_sink_info # /usr/include/pulse/introspect.h:262 -pa_sink_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_sink_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:265 +pa_sink_info = struct_pa_sink_info # /usr/include/pulse/introspect.h:262 +pa_sink_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_sink_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:265 # /usr/include/pulse/introspect.h:268 pa_context_get_sink_info_by_name = _lib.pa_context_get_sink_info_by_name pa_context_get_sink_info_by_name.restype = POINTER(pa_operation) @@ -1556,42 +1656,51 @@ # /usr/include/pulse/introspect.h:277 pa_context_set_sink_volume_by_index = _lib.pa_context_set_sink_volume_by_index pa_context_set_sink_volume_by_index.restype = POINTER(pa_operation) -pa_context_set_sink_volume_by_index.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), pa_context_success_cb_t, POINTER(None)] +pa_context_set_sink_volume_by_index.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:280 pa_context_set_sink_volume_by_name = _lib.pa_context_set_sink_volume_by_name pa_context_set_sink_volume_by_name.restype = POINTER(pa_operation) -pa_context_set_sink_volume_by_name.argtypes = [POINTER(pa_context), c_char_p, POINTER(pa_cvolume), pa_context_success_cb_t, POINTER(None)] +pa_context_set_sink_volume_by_name.argtypes = [POINTER(pa_context), c_char_p, POINTER(pa_cvolume), + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:283 pa_context_set_sink_mute_by_index = _lib.pa_context_set_sink_mute_by_index pa_context_set_sink_mute_by_index.restype = POINTER(pa_operation) -pa_context_set_sink_mute_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_set_sink_mute_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:286 pa_context_set_sink_mute_by_name = _lib.pa_context_set_sink_mute_by_name pa_context_set_sink_mute_by_name.restype = POINTER(pa_operation) -pa_context_set_sink_mute_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_set_sink_mute_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:289 pa_context_suspend_sink_by_name = _lib.pa_context_suspend_sink_by_name pa_context_suspend_sink_by_name.restype = POINTER(pa_operation) -pa_context_suspend_sink_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_suspend_sink_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:292 pa_context_suspend_sink_by_index = _lib.pa_context_suspend_sink_by_index pa_context_suspend_sink_by_index.restype = POINTER(pa_operation) -pa_context_suspend_sink_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_suspend_sink_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:295 pa_context_set_sink_port_by_index = _lib.pa_context_set_sink_port_by_index pa_context_set_sink_port_by_index.restype = POINTER(pa_operation) -pa_context_set_sink_port_by_index.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_set_sink_port_by_index.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:298 pa_context_set_sink_port_by_name = _lib.pa_context_set_sink_port_by_name pa_context_set_sink_port_by_name.restype = POINTER(pa_operation) -pa_context_set_sink_port_by_name.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_set_sink_port_by_name.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_context_success_cb_t, + POINTER(None)] + class struct_pa_source_port_info(Structure): __slots__ = [ @@ -1600,6 +1709,8 @@ 'priority', 'available', ] + + struct_pa_source_port_info._fields_ = [ ('name', c_char_p), ('description', c_char_p), @@ -1607,7 +1718,9 @@ ('available', c_int), ] -pa_source_port_info = struct_pa_source_port_info # /usr/include/pulse/introspect.h:312 +pa_source_port_info = struct_pa_source_port_info # /usr/include/pulse/introspect.h:312 + + class struct_pa_source_info(Structure): __slots__ = [ 'name', @@ -1635,6 +1748,8 @@ 'n_formats', 'formats', ] + + struct_pa_source_info._fields_ = [ ('name', c_char_p), ('index', c_uint32), @@ -1662,8 +1777,9 @@ ('formats', POINTER(POINTER(pa_format_info))), ] -pa_source_info = struct_pa_source_info # /usr/include/pulse/introspect.h:342 -pa_source_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_source_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:345 +pa_source_info = struct_pa_source_info # /usr/include/pulse/introspect.h:342 +pa_source_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_source_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:345 # /usr/include/pulse/introspect.h:348 pa_context_get_source_info_by_name = _lib.pa_context_get_source_info_by_name pa_context_get_source_info_by_name.restype = POINTER(pa_operation) @@ -1682,42 +1798,51 @@ # /usr/include/pulse/introspect.h:357 pa_context_set_source_volume_by_index = _lib.pa_context_set_source_volume_by_index pa_context_set_source_volume_by_index.restype = POINTER(pa_operation) -pa_context_set_source_volume_by_index.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_volume_by_index.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:360 pa_context_set_source_volume_by_name = _lib.pa_context_set_source_volume_by_name pa_context_set_source_volume_by_name.restype = POINTER(pa_operation) -pa_context_set_source_volume_by_name.argtypes = [POINTER(pa_context), c_char_p, POINTER(pa_cvolume), pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_volume_by_name.argtypes = [POINTER(pa_context), c_char_p, POINTER(pa_cvolume), + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:363 pa_context_set_source_mute_by_index = _lib.pa_context_set_source_mute_by_index pa_context_set_source_mute_by_index.restype = POINTER(pa_operation) -pa_context_set_source_mute_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_mute_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:366 pa_context_set_source_mute_by_name = _lib.pa_context_set_source_mute_by_name pa_context_set_source_mute_by_name.restype = POINTER(pa_operation) -pa_context_set_source_mute_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_mute_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:369 pa_context_suspend_source_by_name = _lib.pa_context_suspend_source_by_name pa_context_suspend_source_by_name.restype = POINTER(pa_operation) -pa_context_suspend_source_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_suspend_source_by_name.argtypes = [POINTER(pa_context), c_char_p, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:372 pa_context_suspend_source_by_index = _lib.pa_context_suspend_source_by_index pa_context_suspend_source_by_index.restype = POINTER(pa_operation) -pa_context_suspend_source_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_suspend_source_by_index.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:375 pa_context_set_source_port_by_index = _lib.pa_context_set_source_port_by_index pa_context_set_source_port_by_index.restype = POINTER(pa_operation) -pa_context_set_source_port_by_index.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_port_by_index.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:378 pa_context_set_source_port_by_name = _lib.pa_context_set_source_port_by_name pa_context_set_source_port_by_name.restype = POINTER(pa_operation) -pa_context_set_source_port_by_name.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_port_by_name.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_context_success_cb_t, + POINTER(None)] + class struct_pa_server_info(Structure): __slots__ = [ @@ -1731,6 +1856,8 @@ 'cookie', 'channel_map', ] + + struct_pa_server_info._fields_ = [ ('user_name', c_char_p), ('host_name', c_char_p), @@ -1743,13 +1870,15 @@ ('channel_map', pa_channel_map), ] -pa_server_info = struct_pa_server_info # /usr/include/pulse/introspect.h:397 -pa_server_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_server_info), POINTER(None)) # /usr/include/pulse/introspect.h:400 +pa_server_info = struct_pa_server_info # /usr/include/pulse/introspect.h:397 +pa_server_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_server_info), + POINTER(None)) # /usr/include/pulse/introspect.h:400 # /usr/include/pulse/introspect.h:403 pa_context_get_server_info = _lib.pa_context_get_server_info pa_context_get_server_info.restype = POINTER(pa_operation) pa_context_get_server_info.argtypes = [POINTER(pa_context), pa_server_info_cb_t, POINTER(None)] + class struct_pa_module_info(Structure): __slots__ = [ 'index', @@ -1759,6 +1888,8 @@ 'auto_unload', 'proplist', ] + + struct_pa_module_info._fields_ = [ ('index', c_uint32), ('name', c_char_p), @@ -1768,8 +1899,9 @@ ('proplist', POINTER(pa_proplist)), ] -pa_module_info = struct_pa_module_info # /usr/include/pulse/introspect.h:421 -pa_module_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_module_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:424 +pa_module_info = struct_pa_module_info # /usr/include/pulse/introspect.h:421 +pa_module_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_module_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:424 # /usr/include/pulse/introspect.h:427 pa_context_get_module_info = _lib.pa_context_get_module_info pa_context_get_module_info.restype = POINTER(pa_operation) @@ -1780,7 +1912,8 @@ pa_context_get_module_info_list.restype = POINTER(pa_operation) pa_context_get_module_info_list.argtypes = [POINTER(pa_context), pa_module_info_cb_t, POINTER(None)] -pa_context_index_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_uint32, POINTER(None)) # /usr/include/pulse/introspect.h:433 +pa_context_index_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_uint32, + POINTER(None)) # /usr/include/pulse/introspect.h:433 # /usr/include/pulse/introspect.h:436 pa_context_load_module = _lib.pa_context_load_module pa_context_load_module.restype = POINTER(pa_operation) @@ -1791,6 +1924,7 @@ pa_context_unload_module.restype = POINTER(pa_operation) pa_context_unload_module.argtypes = [POINTER(pa_context), c_uint32, pa_context_success_cb_t, POINTER(None)] + class struct_pa_client_info(Structure): __slots__ = [ 'index', @@ -1799,6 +1933,8 @@ 'driver', 'proplist', ] + + struct_pa_client_info._fields_ = [ ('index', c_uint32), ('name', c_char_p), @@ -1807,8 +1943,9 @@ ('proplist', POINTER(pa_proplist)), ] -pa_client_info = struct_pa_client_info # /usr/include/pulse/introspect.h:454 -pa_client_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_client_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:457 +pa_client_info = struct_pa_client_info # /usr/include/pulse/introspect.h:454 +pa_client_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_client_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:457 # /usr/include/pulse/introspect.h:460 pa_context_get_client_info = _lib.pa_context_get_client_info pa_context_get_client_info.restype = POINTER(pa_operation) @@ -1824,6 +1961,7 @@ pa_context_kill_client.restype = POINTER(pa_operation) pa_context_kill_client.argtypes = [POINTER(pa_context), c_uint32, pa_context_success_cb_t, POINTER(None)] + class struct_pa_card_profile_info(Structure): __slots__ = [ 'name', @@ -1832,6 +1970,8 @@ 'n_sources', 'priority', ] + + struct_pa_card_profile_info._fields_ = [ ('name', c_char_p), ('description', c_char_p), @@ -1840,7 +1980,9 @@ ('priority', c_uint32), ] -pa_card_profile_info = struct_pa_card_profile_info # /usr/include/pulse/introspect.h:479 +pa_card_profile_info = struct_pa_card_profile_info # /usr/include/pulse/introspect.h:479 + + class struct_pa_card_profile_info2(Structure): __slots__ = [ 'name', @@ -1850,6 +1992,8 @@ 'priority', 'available', ] + + struct_pa_card_profile_info2._fields_ = [ ('name', c_char_p), ('description', c_char_p), @@ -1859,7 +2003,9 @@ ('available', c_int), ] -pa_card_profile_info2 = struct_pa_card_profile_info2 # /usr/include/pulse/introspect.h:496 +pa_card_profile_info2 = struct_pa_card_profile_info2 # /usr/include/pulse/introspect.h:496 + + class struct_pa_card_port_info(Structure): __slots__ = [ 'name', @@ -1873,6 +2019,8 @@ 'latency_offset', 'profiles2', ] + + struct_pa_card_port_info._fields_ = [ ('name', c_char_p), ('description', c_char_p), @@ -1886,7 +2034,9 @@ ('profiles2', POINTER(POINTER(pa_card_profile_info2))), ] -pa_card_port_info = struct_pa_card_port_info # /usr/include/pulse/introspect.h:512 +pa_card_port_info = struct_pa_card_port_info # /usr/include/pulse/introspect.h:512 + + class struct_pa_card_info(Structure): __slots__ = [ 'index', @@ -1902,6 +2052,8 @@ 'profiles2', 'active_profile2', ] + + struct_pa_card_info._fields_ = [ ('index', c_uint32), ('name', c_char_p), @@ -1917,8 +2069,9 @@ ('active_profile2', POINTER(pa_card_profile_info2)), ] -pa_card_info = struct_pa_card_info # /usr/include/pulse/introspect.h:530 -pa_card_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_card_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:533 +pa_card_info = struct_pa_card_info # /usr/include/pulse/introspect.h:530 +pa_card_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_card_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:533 # /usr/include/pulse/introspect.h:536 pa_context_get_card_info_by_index = _lib.pa_context_get_card_info_by_index pa_context_get_card_info_by_index.restype = POINTER(pa_operation) @@ -1937,17 +2090,21 @@ # /usr/include/pulse/introspect.h:545 pa_context_set_card_profile_by_index = _lib.pa_context_set_card_profile_by_index pa_context_set_card_profile_by_index.restype = POINTER(pa_operation) -pa_context_set_card_profile_by_index.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_set_card_profile_by_index.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:548 pa_context_set_card_profile_by_name = _lib.pa_context_set_card_profile_by_name pa_context_set_card_profile_by_name.restype = POINTER(pa_operation) -pa_context_set_card_profile_by_name.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_set_card_profile_by_name.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:551 pa_context_set_port_latency_offset = _lib.pa_context_set_port_latency_offset pa_context_set_port_latency_offset.restype = POINTER(pa_operation) -pa_context_set_port_latency_offset.argtypes = [POINTER(pa_context), c_char_p, c_char_p, c_int64, pa_context_success_cb_t, POINTER(None)] +pa_context_set_port_latency_offset.argtypes = [POINTER(pa_context), c_char_p, c_char_p, c_int64, + pa_context_success_cb_t, POINTER(None)] + class struct_pa_sink_input_info(Structure): __slots__ = [ @@ -1970,6 +2127,8 @@ 'volume_writable', 'format', ] + + struct_pa_sink_input_info._fields_ = [ ('index', c_uint32), ('name', c_char_p), @@ -1991,8 +2150,9 @@ ('format', POINTER(pa_format_info)), ] -pa_sink_input_info = struct_pa_sink_input_info # /usr/include/pulse/introspect.h:579 -pa_sink_input_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_sink_input_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:582 +pa_sink_input_info = struct_pa_sink_input_info # /usr/include/pulse/introspect.h:579 +pa_sink_input_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_sink_input_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:582 # /usr/include/pulse/introspect.h:585 pa_context_get_sink_input_info = _lib.pa_context_get_sink_input_info pa_context_get_sink_input_info.restype = POINTER(pa_operation) @@ -2006,17 +2166,20 @@ # /usr/include/pulse/introspect.h:591 pa_context_move_sink_input_by_name = _lib.pa_context_move_sink_input_by_name pa_context_move_sink_input_by_name.restype = POINTER(pa_operation) -pa_context_move_sink_input_by_name.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_move_sink_input_by_name.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:594 pa_context_move_sink_input_by_index = _lib.pa_context_move_sink_input_by_index pa_context_move_sink_input_by_index.restype = POINTER(pa_operation) -pa_context_move_sink_input_by_index.argtypes = [POINTER(pa_context), c_uint32, c_uint32, pa_context_success_cb_t, POINTER(None)] +pa_context_move_sink_input_by_index.argtypes = [POINTER(pa_context), c_uint32, c_uint32, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:597 pa_context_set_sink_input_volume = _lib.pa_context_set_sink_input_volume pa_context_set_sink_input_volume.restype = POINTER(pa_operation) -pa_context_set_sink_input_volume.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), pa_context_success_cb_t, POINTER(None)] +pa_context_set_sink_input_volume.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:600 pa_context_set_sink_input_mute = _lib.pa_context_set_sink_input_mute @@ -2028,6 +2191,7 @@ pa_context_kill_sink_input.restype = POINTER(pa_operation) pa_context_kill_sink_input.argtypes = [POINTER(pa_context), c_uint32, pa_context_success_cb_t, POINTER(None)] + class struct_pa_source_output_info(Structure): __slots__ = [ 'index', @@ -2049,6 +2213,8 @@ 'volume_writable', 'format', ] + + struct_pa_source_output_info._fields_ = [ ('index', c_uint32), ('name', c_char_p), @@ -2070,8 +2236,9 @@ ('format', POINTER(pa_format_info)), ] -pa_source_output_info = struct_pa_source_output_info # /usr/include/pulse/introspect.h:631 -pa_source_output_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_source_output_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:634 +pa_source_output_info = struct_pa_source_output_info # /usr/include/pulse/introspect.h:631 +pa_source_output_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_source_output_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:634 # /usr/include/pulse/introspect.h:637 pa_context_get_source_output_info = _lib.pa_context_get_source_output_info pa_context_get_source_output_info.restype = POINTER(pa_operation) @@ -2085,28 +2252,33 @@ # /usr/include/pulse/introspect.h:643 pa_context_move_source_output_by_name = _lib.pa_context_move_source_output_by_name pa_context_move_source_output_by_name.restype = POINTER(pa_operation) -pa_context_move_source_output_by_name.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, POINTER(None)] +pa_context_move_source_output_by_name.argtypes = [POINTER(pa_context), c_uint32, c_char_p, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:646 pa_context_move_source_output_by_index = _lib.pa_context_move_source_output_by_index pa_context_move_source_output_by_index.restype = POINTER(pa_operation) -pa_context_move_source_output_by_index.argtypes = [POINTER(pa_context), c_uint32, c_uint32, pa_context_success_cb_t, POINTER(None)] +pa_context_move_source_output_by_index.argtypes = [POINTER(pa_context), c_uint32, c_uint32, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:649 pa_context_set_source_output_volume = _lib.pa_context_set_source_output_volume pa_context_set_source_output_volume.restype = POINTER(pa_operation) -pa_context_set_source_output_volume.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_output_volume.argtypes = [POINTER(pa_context), c_uint32, POINTER(pa_cvolume), + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:652 pa_context_set_source_output_mute = _lib.pa_context_set_source_output_mute pa_context_set_source_output_mute.restype = POINTER(pa_operation) -pa_context_set_source_output_mute.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, POINTER(None)] +pa_context_set_source_output_mute.argtypes = [POINTER(pa_context), c_uint32, c_int, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/introspect.h:655 pa_context_kill_source_output = _lib.pa_context_kill_source_output pa_context_kill_source_output.restype = POINTER(pa_operation) pa_context_kill_source_output.argtypes = [POINTER(pa_context), c_uint32, pa_context_success_cb_t, POINTER(None)] + class struct_pa_stat_info(Structure): __slots__ = [ 'memblock_total', @@ -2115,6 +2287,8 @@ 'memblock_allocated_size', 'scache_size', ] + + struct_pa_stat_info._fields_ = [ ('memblock_total', c_uint32), ('memblock_total_size', c_uint32), @@ -2123,13 +2297,15 @@ ('scache_size', c_uint32), ] -pa_stat_info = struct_pa_stat_info # /usr/include/pulse/introspect.h:670 -pa_stat_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_stat_info), POINTER(None)) # /usr/include/pulse/introspect.h:673 +pa_stat_info = struct_pa_stat_info # /usr/include/pulse/introspect.h:670 +pa_stat_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_stat_info), + POINTER(None)) # /usr/include/pulse/introspect.h:673 # /usr/include/pulse/introspect.h:676 pa_context_stat = _lib.pa_context_stat pa_context_stat.restype = POINTER(pa_operation) pa_context_stat.argtypes = [POINTER(pa_context), pa_stat_info_cb_t, POINTER(None)] + class struct_pa_sample_info(Structure): __slots__ = [ 'index', @@ -2143,6 +2319,8 @@ 'filename', 'proplist', ] + + struct_pa_sample_info._fields_ = [ ('index', c_uint32), ('name', c_char_p), @@ -2156,8 +2334,9 @@ ('proplist', POINTER(pa_proplist)), ] -pa_sample_info = struct_pa_sample_info # /usr/include/pulse/introspect.h:696 -pa_sample_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_sample_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:699 +pa_sample_info = struct_pa_sample_info # /usr/include/pulse/introspect.h:696 +pa_sample_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_sample_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:699 # /usr/include/pulse/introspect.h:702 pa_context_get_sample_info_by_name = _lib.pa_context_get_sample_info_by_name pa_context_get_sample_info_by_name.restype = POINTER(pa_operation) @@ -2176,7 +2355,9 @@ enum_pa_autoload_type = c_int PA_AUTOLOAD_SINK = 0 PA_AUTOLOAD_SOURCE = 1 -pa_autoload_type_t = enum_pa_autoload_type # /usr/include/pulse/introspect.h:720 +pa_autoload_type_t = enum_pa_autoload_type # /usr/include/pulse/introspect.h:720 + + class struct_pa_autoload_info(Structure): __slots__ = [ 'index', @@ -2185,6 +2366,8 @@ 'module', 'argument', ] + + struct_pa_autoload_info._fields_ = [ ('index', c_uint32), ('name', c_char_p), @@ -2193,12 +2376,14 @@ ('argument', c_char_p), ] -pa_autoload_info = struct_pa_autoload_info # /usr/include/pulse/introspect.h:731 -pa_autoload_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_autoload_info), c_int, POINTER(None)) # /usr/include/pulse/introspect.h:734 +pa_autoload_info = struct_pa_autoload_info # /usr/include/pulse/introspect.h:731 +pa_autoload_info_cb_t = CFUNCTYPE(None, POINTER(pa_context), POINTER(pa_autoload_info), c_int, + POINTER(None)) # /usr/include/pulse/introspect.h:734 # /usr/include/pulse/introspect.h:737 pa_context_get_autoload_info_by_name = _lib.pa_context_get_autoload_info_by_name pa_context_get_autoload_info_by_name.restype = POINTER(pa_operation) -pa_context_get_autoload_info_by_name.argtypes = [POINTER(pa_context), c_char_p, pa_autoload_type_t, pa_autoload_info_cb_t, POINTER(None)] +pa_context_get_autoload_info_by_name.argtypes = [POINTER(pa_context), c_char_p, pa_autoload_type_t, + pa_autoload_info_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:740 pa_context_get_autoload_info_by_index = _lib.pa_context_get_autoload_info_by_index @@ -2213,19 +2398,22 @@ # /usr/include/pulse/introspect.h:746 pa_context_add_autoload = _lib.pa_context_add_autoload pa_context_add_autoload.restype = POINTER(pa_operation) -pa_context_add_autoload.argtypes = [POINTER(pa_context), c_char_p, pa_autoload_type_t, c_char_p, c_char_p, pa_context_index_cb_t, POINTER(None)] +pa_context_add_autoload.argtypes = [POINTER(pa_context), c_char_p, pa_autoload_type_t, c_char_p, c_char_p, + pa_context_index_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:749 pa_context_remove_autoload_by_name = _lib.pa_context_remove_autoload_by_name pa_context_remove_autoload_by_name.restype = POINTER(pa_operation) -pa_context_remove_autoload_by_name.argtypes = [POINTER(pa_context), c_char_p, pa_autoload_type_t, pa_context_success_cb_t, POINTER(None)] +pa_context_remove_autoload_by_name.argtypes = [POINTER(pa_context), c_char_p, pa_autoload_type_t, + pa_context_success_cb_t, POINTER(None)] # /usr/include/pulse/introspect.h:752 pa_context_remove_autoload_by_index = _lib.pa_context_remove_autoload_by_index pa_context_remove_autoload_by_index.restype = POINTER(pa_operation) pa_context_remove_autoload_by_index.argtypes = [POINTER(pa_context), c_uint32, pa_context_success_cb_t, POINTER(None)] -pa_context_subscribe_cb_t = CFUNCTYPE(None, POINTER(pa_context), pa_subscription_event_type_t, c_uint32, POINTER(None)) # /usr/include/pulse/subscribe.h:73 +pa_context_subscribe_cb_t = CFUNCTYPE(None, POINTER(pa_context), pa_subscription_event_type_t, c_uint32, + POINTER(None)) # /usr/include/pulse/subscribe.h:73 # /usr/include/pulse/subscribe.h:76 pa_context_subscribe = _lib.pa_context_subscribe pa_context_subscribe.restype = POINTER(pa_operation) @@ -2236,7 +2424,8 @@ pa_context_set_subscribe_callback.restype = None pa_context_set_subscribe_callback.argtypes = [POINTER(pa_context), pa_context_subscribe_cb_t, POINTER(None)] -pa_context_play_sample_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_uint32, POINTER(None)) # /usr/include/pulse/scache.h:85 +pa_context_play_sample_cb_t = CFUNCTYPE(None, POINTER(pa_context), c_uint32, + POINTER(None)) # /usr/include/pulse/scache.h:85 # /usr/include/pulse/scache.h:88 pa_stream_connect_upload = _lib.pa_stream_connect_upload pa_stream_connect_upload.restype = c_int @@ -2255,12 +2444,14 @@ # /usr/include/pulse/scache.h:101 pa_context_play_sample = _lib.pa_context_play_sample pa_context_play_sample.restype = POINTER(pa_operation) -pa_context_play_sample.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_volume_t, pa_context_success_cb_t, POINTER(None)] +pa_context_play_sample.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_volume_t, pa_context_success_cb_t, + POINTER(None)] # /usr/include/pulse/scache.h:113 pa_context_play_sample_with_proplist = _lib.pa_context_play_sample_with_proplist pa_context_play_sample_with_proplist.restype = POINTER(pa_operation) -pa_context_play_sample_with_proplist.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_volume_t, POINTER(pa_proplist), pa_context_play_sample_cb_t, POINTER(None)] +pa_context_play_sample_with_proplist.argtypes = [POINTER(pa_context), c_char_p, c_char_p, pa_volume_t, + POINTER(pa_proplist), pa_context_play_sample_cb_t, POINTER(None)] # /usr/include/pulse/error.h:33 pa_strerror = _lib.pa_strerror @@ -2332,21 +2523,27 @@ pa_locale_to_utf8.restype = c_char_p pa_locale_to_utf8.argtypes = [c_char_p] + class struct_pa_threaded_mainloop(Structure): __slots__ = [ ] + + struct_pa_threaded_mainloop._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_threaded_mainloop(Structure): __slots__ = [ ] + + struct_pa_threaded_mainloop._fields_ = [ ('_opaque_struct', c_int) ] -pa_threaded_mainloop = struct_pa_threaded_mainloop # /usr/include/pulse/thread-mainloop.h:246 +pa_threaded_mainloop = struct_pa_threaded_mainloop # /usr/include/pulse/thread-mainloop.h:246 # /usr/include/pulse/thread-mainloop.h:251 pa_threaded_mainloop_new = _lib.pa_threaded_mainloop_new pa_threaded_mainloop_new.restype = POINTER(pa_threaded_mainloop) @@ -2407,26 +2604,32 @@ pa_threaded_mainloop_in_thread.restype = c_int pa_threaded_mainloop_in_thread.argtypes = [POINTER(pa_threaded_mainloop)] + # /usr/include/pulse/thread-mainloop.h:313 -#pa_threaded_mainloop_set_name = _lib.pa_threaded_mainloop_set_name -#pa_threaded_mainloop_set_name.restype = None -#pa_threaded_mainloop_set_name.argtypes = [POINTER(pa_threaded_mainloop), c_char_p] +# pa_threaded_mainloop_set_name = _lib.pa_threaded_mainloop_set_name +# pa_threaded_mainloop_set_name.restype = None +# pa_threaded_mainloop_set_name.argtypes = [POINTER(pa_threaded_mainloop), c_char_p] class struct_pa_mainloop(Structure): __slots__ = [ ] + + struct_pa_mainloop._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_mainloop(Structure): __slots__ = [ ] + + struct_pa_mainloop._fields_ = [ ('_opaque_struct', c_int) ] -pa_mainloop = struct_pa_mainloop # /usr/include/pulse/mainloop.h:78 +pa_mainloop = struct_pa_mainloop # /usr/include/pulse/mainloop.h:78 # /usr/include/pulse/mainloop.h:81 pa_mainloop_new = _lib.pa_mainloop_new pa_mainloop_new.restype = POINTER(pa_mainloop) @@ -2482,43 +2685,58 @@ pa_mainloop_wakeup.restype = None pa_mainloop_wakeup.argtypes = [POINTER(pa_mainloop)] + class struct_pollfd(Structure): __slots__ = [ ] + + struct_pollfd._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pollfd(Structure): __slots__ = [ ] + + struct_pollfd._fields_ = [ ('_opaque_struct', c_int) ] -pa_poll_func = CFUNCTYPE(c_int, POINTER(struct_pollfd), c_ulong, c_int, POINTER(None)) # /usr/include/pulse/mainloop.h:124 +pa_poll_func = CFUNCTYPE(c_int, POINTER(struct_pollfd), c_ulong, c_int, + POINTER(None)) # /usr/include/pulse/mainloop.h:124 # /usr/include/pulse/mainloop.h:127 pa_mainloop_set_poll_func = _lib.pa_mainloop_set_poll_func pa_mainloop_set_poll_func.restype = None pa_mainloop_set_poll_func.argtypes = [POINTER(pa_mainloop), pa_poll_func, POINTER(None)] + class struct_pa_signal_event(Structure): __slots__ = [ ] + + struct_pa_signal_event._fields_ = [ ('_opaque_struct', c_int) ] + class struct_pa_signal_event(Structure): __slots__ = [ ] + + struct_pa_signal_event._fields_ = [ ('_opaque_struct', c_int) ] -pa_signal_event = struct_pa_signal_event # /usr/include/pulse/mainloop-signal.h:39 -pa_signal_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_signal_event), c_int, POINTER(None)) # /usr/include/pulse/mainloop-signal.h:42 -pa_signal_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_signal_event), POINTER(None)) # /usr/include/pulse/mainloop-signal.h:45 +pa_signal_event = struct_pa_signal_event # /usr/include/pulse/mainloop-signal.h:39 +pa_signal_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_signal_event), c_int, + POINTER(None)) # /usr/include/pulse/mainloop-signal.h:42 +pa_signal_destroy_cb_t = CFUNCTYPE(None, POINTER(pa_mainloop_api), POINTER(pa_signal_event), + POINTER(None)) # /usr/include/pulse/mainloop-signal.h:45 # /usr/include/pulse/mainloop-signal.h:48 pa_signal_init = _lib.pa_signal_init pa_signal_init.restype = c_int @@ -2579,304 +2797,295 @@ pa_msleep.restype = c_int pa_msleep.argtypes = [c_ulong] - # /usr/include/pulse/timeval.h:61 pa_gettimeofday = _lib.pa_gettimeofday pa_gettimeofday.restype = POINTER(struct_timeval) pa_gettimeofday.argtypes = [POINTER(struct_timeval)] - # /usr/include/pulse/timeval.h:65 pa_timeval_diff = _lib.pa_timeval_diff pa_timeval_diff.restype = pa_usec_t pa_timeval_diff.argtypes = [POINTER(struct_timeval), POINTER(struct_timeval)] - # /usr/include/pulse/timeval.h:68 pa_timeval_cmp = _lib.pa_timeval_cmp pa_timeval_cmp.restype = c_int pa_timeval_cmp.argtypes = [POINTER(struct_timeval), POINTER(struct_timeval)] - # /usr/include/pulse/timeval.h:71 pa_timeval_age = _lib.pa_timeval_age pa_timeval_age.restype = pa_usec_t pa_timeval_age.argtypes = [POINTER(struct_timeval)] - # /usr/include/pulse/timeval.h:74 pa_timeval_add = _lib.pa_timeval_add pa_timeval_add.restype = POINTER(struct_timeval) pa_timeval_add.argtypes = [POINTER(struct_timeval), pa_usec_t] - # /usr/include/pulse/timeval.h:77 pa_timeval_sub = _lib.pa_timeval_sub pa_timeval_sub.restype = POINTER(struct_timeval) pa_timeval_sub.argtypes = [POINTER(struct_timeval), pa_usec_t] - # /usr/include/pulse/timeval.h:80 pa_timeval_store = _lib.pa_timeval_store pa_timeval_store.restype = POINTER(struct_timeval) pa_timeval_store.argtypes = [POINTER(struct_timeval), pa_usec_t] - # /usr/include/pulse/timeval.h:83 pa_timeval_load = _lib.pa_timeval_load pa_timeval_load.restype = pa_usec_t pa_timeval_load.argtypes = [POINTER(struct_timeval)] - __all__ = ['pa_get_library_version', 'PA_API_VERSION', 'PA_PROTOCOL_VERSION', -'PA_MAJOR', 'PA_MINOR', 'PA_MICRO', 'PA_CHANNELS_MAX', 'PA_RATE_MAX', -'pa_sample_format_t', 'PA_SAMPLE_U8', 'PA_SAMPLE_ALAW', 'PA_SAMPLE_ULAW', -'PA_SAMPLE_S16LE', 'PA_SAMPLE_S16BE', 'PA_SAMPLE_FLOAT32LE', -'PA_SAMPLE_FLOAT32BE', 'PA_SAMPLE_S32LE', 'PA_SAMPLE_S32BE', -'PA_SAMPLE_S24LE', 'PA_SAMPLE_S24BE', 'PA_SAMPLE_S24_32LE', -'PA_SAMPLE_S24_32BE', 'PA_SAMPLE_MAX', 'PA_SAMPLE_INVALID', 'pa_sample_spec', -'pa_usec_t', 'pa_bytes_per_second', 'pa_frame_size', 'pa_sample_size', -'pa_sample_size_of_format', 'pa_bytes_to_usec', 'pa_usec_to_bytes', -'pa_sample_spec_init', 'pa_sample_format_valid', 'pa_sample_rate_valid', -'pa_channels_valid', 'pa_sample_spec_valid', 'pa_sample_spec_equal', -'pa_sample_format_to_string', 'pa_parse_sample_format', -'PA_SAMPLE_SPEC_SNPRINT_MAX', 'pa_sample_spec_snprint', -'PA_BYTES_SNPRINT_MAX', 'pa_bytes_snprint', 'pa_sample_format_is_le', -'pa_sample_format_is_be', 'pa_context_state_t', 'PA_CONTEXT_UNCONNECTED', -'PA_CONTEXT_CONNECTING', 'PA_CONTEXT_AUTHORIZING', 'PA_CONTEXT_SETTING_NAME', -'PA_CONTEXT_READY', 'PA_CONTEXT_FAILED', 'PA_CONTEXT_TERMINATED', -'pa_stream_state_t', 'PA_STREAM_UNCONNECTED', 'PA_STREAM_CREATING', -'PA_STREAM_READY', 'PA_STREAM_FAILED', 'PA_STREAM_TERMINATED', -'pa_operation_state_t', 'PA_OPERATION_RUNNING', 'PA_OPERATION_DONE', -'PA_OPERATION_CANCELLED', 'pa_context_flags_t', 'PA_CONTEXT_NOFLAGS', -'PA_CONTEXT_NOAUTOSPAWN', 'PA_CONTEXT_NOFAIL', 'pa_direction_t', -'PA_DIRECTION_OUTPUT', 'PA_DIRECTION_INPUT', 'pa_device_type_t', -'PA_DEVICE_TYPE_SINK', 'PA_DEVICE_TYPE_SOURCE', 'pa_stream_direction_t', -'PA_STREAM_NODIRECTION', 'PA_STREAM_PLAYBACK', 'PA_STREAM_RECORD', -'PA_STREAM_UPLOAD', 'pa_stream_flags_t', 'PA_STREAM_NOFLAGS', -'PA_STREAM_START_CORKED', 'PA_STREAM_INTERPOLATE_TIMING', -'PA_STREAM_NOT_MONOTONIC', 'PA_STREAM_AUTO_TIMING_UPDATE', -'PA_STREAM_NO_REMAP_CHANNELS', 'PA_STREAM_NO_REMIX_CHANNELS', -'PA_STREAM_FIX_FORMAT', 'PA_STREAM_FIX_RATE', 'PA_STREAM_FIX_CHANNELS', -'PA_STREAM_DONT_MOVE', 'PA_STREAM_VARIABLE_RATE', 'PA_STREAM_PEAK_DETECT', -'PA_STREAM_START_MUTED', 'PA_STREAM_ADJUST_LATENCY', -'PA_STREAM_EARLY_REQUESTS', 'PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND', -'PA_STREAM_START_UNMUTED', 'PA_STREAM_FAIL_ON_SUSPEND', -'PA_STREAM_RELATIVE_VOLUME', 'PA_STREAM_PASSTHROUGH', 'pa_buffer_attr', -'pa_error_code_t', 'PA_OK', 'PA_ERR_ACCESS', 'PA_ERR_COMMAND', -'PA_ERR_INVALID', 'PA_ERR_EXIST', 'PA_ERR_NOENTITY', -'PA_ERR_CONNECTIONREFUSED', 'PA_ERR_PROTOCOL', 'PA_ERR_TIMEOUT', -'PA_ERR_AUTHKEY', 'PA_ERR_INTERNAL', 'PA_ERR_CONNECTIONTERMINATED', -'PA_ERR_KILLED', 'PA_ERR_INVALIDSERVER', 'PA_ERR_MODINITFAILED', -'PA_ERR_BADSTATE', 'PA_ERR_NODATA', 'PA_ERR_VERSION', 'PA_ERR_TOOLARGE', -'PA_ERR_NOTSUPPORTED', 'PA_ERR_UNKNOWN', 'PA_ERR_NOEXTENSION', -'PA_ERR_OBSOLETE', 'PA_ERR_NOTIMPLEMENTED', 'PA_ERR_FORKED', 'PA_ERR_IO', -'PA_ERR_BUSY', 'PA_ERR_MAX', 'pa_subscription_mask_t', -'PA_SUBSCRIPTION_MASK_NULL', 'PA_SUBSCRIPTION_MASK_SINK', -'PA_SUBSCRIPTION_MASK_SOURCE', 'PA_SUBSCRIPTION_MASK_SINK_INPUT', -'PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT', 'PA_SUBSCRIPTION_MASK_MODULE', -'PA_SUBSCRIPTION_MASK_CLIENT', 'PA_SUBSCRIPTION_MASK_SAMPLE_CACHE', -'PA_SUBSCRIPTION_MASK_SERVER', 'PA_SUBSCRIPTION_MASK_AUTOLOAD', -'PA_SUBSCRIPTION_MASK_CARD', 'PA_SUBSCRIPTION_MASK_ALL', -'pa_subscription_event_type_t', 'PA_SUBSCRIPTION_EVENT_SINK', -'PA_SUBSCRIPTION_EVENT_SOURCE', 'PA_SUBSCRIPTION_EVENT_SINK_INPUT', -'PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT', 'PA_SUBSCRIPTION_EVENT_MODULE', -'PA_SUBSCRIPTION_EVENT_CLIENT', 'PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE', -'PA_SUBSCRIPTION_EVENT_SERVER', 'PA_SUBSCRIPTION_EVENT_AUTOLOAD', -'PA_SUBSCRIPTION_EVENT_CARD', 'PA_SUBSCRIPTION_EVENT_FACILITY_MASK', -'PA_SUBSCRIPTION_EVENT_NEW', 'PA_SUBSCRIPTION_EVENT_CHANGE', -'PA_SUBSCRIPTION_EVENT_REMOVE', 'PA_SUBSCRIPTION_EVENT_TYPE_MASK', -'pa_timing_info', 'pa_spawn_api', 'pa_seek_mode_t', 'PA_SEEK_RELATIVE', -'PA_SEEK_ABSOLUTE', 'PA_SEEK_RELATIVE_ON_READ', 'PA_SEEK_RELATIVE_END', -'pa_sink_flags_t', 'PA_SINK_NOFLAGS', 'PA_SINK_HW_VOLUME_CTRL', -'PA_SINK_LATENCY', 'PA_SINK_HARDWARE', 'PA_SINK_NETWORK', -'PA_SINK_HW_MUTE_CTRL', 'PA_SINK_DECIBEL_VOLUME', 'PA_SINK_FLAT_VOLUME', -'PA_SINK_DYNAMIC_LATENCY', 'PA_SINK_SET_FORMATS', 'pa_sink_state_t', -'PA_SINK_INVALID_STATE', 'PA_SINK_RUNNING', 'PA_SINK_IDLE', -'PA_SINK_SUSPENDED', 'PA_SINK_INIT', 'PA_SINK_UNLINKED', 'pa_source_flags_t', -'PA_SOURCE_NOFLAGS', 'PA_SOURCE_HW_VOLUME_CTRL', 'PA_SOURCE_LATENCY', -'PA_SOURCE_HARDWARE', 'PA_SOURCE_NETWORK', 'PA_SOURCE_HW_MUTE_CTRL', -'PA_SOURCE_DECIBEL_VOLUME', 'PA_SOURCE_DYNAMIC_LATENCY', -'PA_SOURCE_FLAT_VOLUME', 'pa_source_state_t', 'PA_SOURCE_INVALID_STATE', -'PA_SOURCE_RUNNING', 'PA_SOURCE_IDLE', 'PA_SOURCE_SUSPENDED', -'PA_SOURCE_INIT', 'PA_SOURCE_UNLINKED', 'pa_free_cb_t', 'pa_port_available_t', -'PA_PORT_AVAILABLE_UNKNOWN', 'PA_PORT_AVAILABLE_NO', 'PA_PORT_AVAILABLE_YES', -'pa_mainloop_api', 'pa_io_event_flags_t', 'PA_IO_EVENT_NULL', -'PA_IO_EVENT_INPUT', 'PA_IO_EVENT_OUTPUT', 'PA_IO_EVENT_HANGUP', -'PA_IO_EVENT_ERROR', 'pa_io_event', 'pa_io_event_cb_t', -'pa_io_event_destroy_cb_t', 'pa_time_event', 'pa_time_event_cb_t', -'pa_time_event_destroy_cb_t', 'pa_defer_event', 'pa_defer_event_cb_t', -'pa_defer_event_destroy_cb_t', 'pa_mainloop_api_once', -'pa_channel_position_t', 'PA_CHANNEL_POSITION_INVALID', -'PA_CHANNEL_POSITION_MONO', 'PA_CHANNEL_POSITION_FRONT_LEFT', -'PA_CHANNEL_POSITION_FRONT_RIGHT', 'PA_CHANNEL_POSITION_FRONT_CENTER', -'PA_CHANNEL_POSITION_LEFT', 'PA_CHANNEL_POSITION_RIGHT', -'PA_CHANNEL_POSITION_CENTER', 'PA_CHANNEL_POSITION_REAR_CENTER', -'PA_CHANNEL_POSITION_REAR_LEFT', 'PA_CHANNEL_POSITION_REAR_RIGHT', -'PA_CHANNEL_POSITION_LFE', 'PA_CHANNEL_POSITION_SUBWOOFER', -'PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER', -'PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER', 'PA_CHANNEL_POSITION_SIDE_LEFT', -'PA_CHANNEL_POSITION_SIDE_RIGHT', 'PA_CHANNEL_POSITION_AUX0', -'PA_CHANNEL_POSITION_AUX1', 'PA_CHANNEL_POSITION_AUX2', -'PA_CHANNEL_POSITION_AUX3', 'PA_CHANNEL_POSITION_AUX4', -'PA_CHANNEL_POSITION_AUX5', 'PA_CHANNEL_POSITION_AUX6', -'PA_CHANNEL_POSITION_AUX7', 'PA_CHANNEL_POSITION_AUX8', -'PA_CHANNEL_POSITION_AUX9', 'PA_CHANNEL_POSITION_AUX10', -'PA_CHANNEL_POSITION_AUX11', 'PA_CHANNEL_POSITION_AUX12', -'PA_CHANNEL_POSITION_AUX13', 'PA_CHANNEL_POSITION_AUX14', -'PA_CHANNEL_POSITION_AUX15', 'PA_CHANNEL_POSITION_AUX16', -'PA_CHANNEL_POSITION_AUX17', 'PA_CHANNEL_POSITION_AUX18', -'PA_CHANNEL_POSITION_AUX19', 'PA_CHANNEL_POSITION_AUX20', -'PA_CHANNEL_POSITION_AUX21', 'PA_CHANNEL_POSITION_AUX22', -'PA_CHANNEL_POSITION_AUX23', 'PA_CHANNEL_POSITION_AUX24', -'PA_CHANNEL_POSITION_AUX25', 'PA_CHANNEL_POSITION_AUX26', -'PA_CHANNEL_POSITION_AUX27', 'PA_CHANNEL_POSITION_AUX28', -'PA_CHANNEL_POSITION_AUX29', 'PA_CHANNEL_POSITION_AUX30', -'PA_CHANNEL_POSITION_AUX31', 'PA_CHANNEL_POSITION_TOP_CENTER', -'PA_CHANNEL_POSITION_TOP_FRONT_LEFT', 'PA_CHANNEL_POSITION_TOP_FRONT_RIGHT', -'PA_CHANNEL_POSITION_TOP_FRONT_CENTER', 'PA_CHANNEL_POSITION_TOP_REAR_LEFT', -'PA_CHANNEL_POSITION_TOP_REAR_RIGHT', 'PA_CHANNEL_POSITION_TOP_REAR_CENTER', -'PA_CHANNEL_POSITION_MAX', 'pa_channel_position_mask_t', -'pa_channel_map_def_t', 'PA_CHANNEL_MAP_AIFF', 'PA_CHANNEL_MAP_ALSA', -'PA_CHANNEL_MAP_AUX', 'PA_CHANNEL_MAP_WAVEEX', 'PA_CHANNEL_MAP_OSS', -'PA_CHANNEL_MAP_DEF_MAX', 'PA_CHANNEL_MAP_DEFAULT', 'pa_channel_map', -'pa_channel_map_init', 'pa_channel_map_init_mono', -'pa_channel_map_init_stereo', 'pa_channel_map_init_auto', -'pa_channel_map_init_extend', 'pa_channel_position_to_string', -'pa_channel_position_from_string', 'pa_channel_position_to_pretty_string', -'PA_CHANNEL_MAP_SNPRINT_MAX', 'pa_channel_map_snprint', -'pa_channel_map_parse', 'pa_channel_map_equal', 'pa_channel_map_valid', -'pa_channel_map_compatible', 'pa_channel_map_superset', -'pa_channel_map_can_balance', 'pa_channel_map_can_fade', -'pa_channel_map_to_name', 'pa_channel_map_to_pretty_name', -'pa_channel_map_has_position', 'pa_channel_map_mask', 'pa_operation', -'pa_operation_notify_cb_t', 'pa_operation_ref', 'pa_operation_unref', -'pa_operation_cancel', 'pa_operation_get_state', -'pa_operation_set_state_callback', 'pa_context', 'pa_context_notify_cb_t', -'pa_context_success_cb_t', 'pa_context_event_cb_t', 'pa_context_new', -'pa_context_new_with_proplist', 'pa_context_unref', 'pa_context_ref', -'pa_context_set_state_callback', 'pa_context_set_event_callback', -'pa_context_errno', 'pa_context_is_pending', 'pa_context_get_state', -'pa_context_connect', 'pa_context_disconnect', 'pa_context_drain', -'pa_context_exit_daemon', 'pa_context_set_default_sink', -'pa_context_set_default_source', 'pa_context_is_local', 'pa_context_set_name', -'pa_context_get_server', 'pa_context_get_protocol_version', -'pa_context_get_server_protocol_version', 'PA_UPDATE_SET', 'PA_UPDATE_MERGE', -'PA_UPDATE_REPLACE', 'pa_context_proplist_update', -'pa_context_proplist_remove', 'pa_context_get_index', 'pa_context_rttime_new', -'pa_context_rttime_restart', 'pa_context_get_tile_size', -'pa_context_load_cookie_from_file', 'pa_volume_t', 'pa_cvolume', -'pa_cvolume_equal', 'pa_cvolume_init', 'pa_cvolume_set', -'PA_CVOLUME_SNPRINT_MAX', 'pa_cvolume_snprint', -'PA_SW_CVOLUME_SNPRINT_DB_MAX', 'pa_sw_cvolume_snprint_dB', -'PA_CVOLUME_SNPRINT_VERBOSE_MAX', 'pa_cvolume_snprint_verbose', -'PA_VOLUME_SNPRINT_MAX', 'pa_volume_snprint', 'PA_SW_VOLUME_SNPRINT_DB_MAX', -'pa_sw_volume_snprint_dB', 'PA_VOLUME_SNPRINT_VERBOSE_MAX', -'pa_volume_snprint_verbose', 'pa_cvolume_avg', 'pa_cvolume_avg_mask', -'pa_cvolume_max', 'pa_cvolume_max_mask', 'pa_cvolume_min', -'pa_cvolume_min_mask', 'pa_cvolume_valid', 'pa_cvolume_channels_equal_to', -'pa_sw_volume_multiply', 'pa_sw_cvolume_multiply', -'pa_sw_cvolume_multiply_scalar', 'pa_sw_volume_divide', -'pa_sw_cvolume_divide', 'pa_sw_cvolume_divide_scalar', 'pa_sw_volume_from_dB', -'pa_sw_volume_to_dB', 'pa_sw_volume_from_linear', 'pa_sw_volume_to_linear', -'pa_cvolume_remap', 'pa_cvolume_compatible', -'pa_cvolume_compatible_with_channel_map', 'pa_cvolume_get_balance', -'pa_cvolume_set_balance', 'pa_cvolume_get_fade', 'pa_cvolume_set_fade', -'pa_cvolume_scale', 'pa_cvolume_scale_mask', 'pa_cvolume_set_position', -'pa_cvolume_get_position', 'pa_cvolume_merge', 'pa_cvolume_inc_clamp', -'pa_cvolume_inc', 'pa_cvolume_dec', 'pa_stream', 'pa_stream_success_cb_t', -'pa_stream_request_cb_t', 'pa_stream_notify_cb_t', 'pa_stream_event_cb_t', -'pa_stream_new', 'pa_stream_new_with_proplist', 'PA_ENCODING_ANY', -'PA_ENCODING_PCM', 'PA_ENCODING_AC3_IEC61937', 'PA_ENCODING_EAC3_IEC61937', -'PA_ENCODING_MPEG_IEC61937', 'PA_ENCODING_DTS_IEC61937', -'PA_ENCODING_MPEG2_AAC_IEC61937', 'PA_ENCODING_MAX', 'PA_ENCODING_INVALID', -'pa_stream_new_extended', 'pa_stream_unref', 'pa_stream_ref', -'pa_stream_get_state', 'pa_stream_get_context', 'pa_stream_get_index', -'pa_stream_get_device_index', 'pa_stream_get_device_name', -'pa_stream_is_suspended', 'pa_stream_is_corked', 'pa_stream_connect_playback', -'pa_stream_connect_record', 'pa_stream_disconnect', 'pa_stream_begin_write', -'pa_stream_cancel_write', 'pa_stream_write', 'pa_stream_write_ext_free', -'pa_stream_peek', 'pa_stream_drop', 'pa_stream_writable_size', -'pa_stream_readable_size', 'pa_stream_drain', 'pa_stream_update_timing_info', -'pa_stream_set_state_callback', 'pa_stream_set_write_callback', -'pa_stream_set_read_callback', 'pa_stream_set_overflow_callback', -'pa_stream_get_underflow_index', 'pa_stream_set_underflow_callback', -'pa_stream_set_started_callback', 'pa_stream_set_latency_update_callback', -'pa_stream_set_moved_callback', 'pa_stream_set_suspended_callback', -'pa_stream_set_event_callback', 'pa_stream_set_buffer_attr_callback', -'pa_stream_cork', 'pa_stream_flush', 'pa_stream_prebuf', 'pa_stream_trigger', -'pa_stream_set_name', 'pa_stream_get_time', 'pa_stream_get_latency', -'pa_stream_get_timing_info', 'pa_stream_get_sample_spec', -'pa_stream_get_channel_map', 'pa_stream_get_format_info', -'pa_stream_get_buffer_attr', 'pa_stream_set_buffer_attr', -'pa_stream_update_sample_rate', 'pa_stream_proplist_update', -'pa_stream_proplist_remove', 'pa_stream_set_monitor_stream', -'pa_stream_get_monitor_stream', 'pa_sink_port_info', 'pa_sink_info', -'pa_sink_info_cb_t', 'pa_context_get_sink_info_by_name', -'pa_context_get_sink_info_by_index', 'pa_context_get_sink_info_list', -'pa_context_set_sink_volume_by_index', 'pa_context_set_sink_volume_by_name', -'pa_context_set_sink_mute_by_index', 'pa_context_set_sink_mute_by_name', -'pa_context_suspend_sink_by_name', 'pa_context_suspend_sink_by_index', -'pa_context_set_sink_port_by_index', 'pa_context_set_sink_port_by_name', -'pa_source_port_info', 'pa_source_info', 'pa_source_info_cb_t', -'pa_context_get_source_info_by_name', 'pa_context_get_source_info_by_index', -'pa_context_get_source_info_list', 'pa_context_set_source_volume_by_index', -'pa_context_set_source_volume_by_name', 'pa_context_set_source_mute_by_index', -'pa_context_set_source_mute_by_name', 'pa_context_suspend_source_by_name', -'pa_context_suspend_source_by_index', 'pa_context_set_source_port_by_index', -'pa_context_set_source_port_by_name', 'pa_server_info', 'pa_server_info_cb_t', -'pa_context_get_server_info', 'pa_module_info', 'pa_module_info_cb_t', -'pa_context_get_module_info', 'pa_context_get_module_info_list', -'pa_context_index_cb_t', 'pa_context_load_module', 'pa_context_unload_module', -'pa_client_info', 'pa_client_info_cb_t', 'pa_context_get_client_info', -'pa_context_get_client_info_list', 'pa_context_kill_client', -'pa_card_profile_info', 'pa_card_profile_info2', 'pa_card_port_info', -'pa_card_info', 'pa_card_info_cb_t', 'pa_context_get_card_info_by_index', -'pa_context_get_card_info_by_name', 'pa_context_get_card_info_list', -'pa_context_set_card_profile_by_index', 'pa_context_set_card_profile_by_name', -'pa_context_set_port_latency_offset', 'pa_sink_input_info', -'pa_sink_input_info_cb_t', 'pa_context_get_sink_input_info', -'pa_context_get_sink_input_info_list', 'pa_context_move_sink_input_by_name', -'pa_context_move_sink_input_by_index', 'pa_context_set_sink_input_volume', -'pa_context_set_sink_input_mute', 'pa_context_kill_sink_input', -'pa_source_output_info', 'pa_source_output_info_cb_t', -'pa_context_get_source_output_info', 'pa_context_get_source_output_info_list', -'pa_context_move_source_output_by_name', -'pa_context_move_source_output_by_index', -'pa_context_set_source_output_volume', 'pa_context_set_source_output_mute', -'pa_context_kill_source_output', 'pa_stat_info', 'pa_stat_info_cb_t', -'pa_context_stat', 'pa_sample_info', 'pa_sample_info_cb_t', -'pa_context_get_sample_info_by_name', 'pa_context_get_sample_info_by_index', -'pa_context_get_sample_info_list', 'pa_autoload_type_t', 'PA_AUTOLOAD_SINK', -'PA_AUTOLOAD_SOURCE', 'pa_autoload_info', 'pa_autoload_info_cb_t', -'pa_context_get_autoload_info_by_name', -'pa_context_get_autoload_info_by_index', 'pa_context_get_autoload_info_list', -'pa_context_add_autoload', 'pa_context_remove_autoload_by_name', -'pa_context_remove_autoload_by_index', 'pa_context_subscribe_cb_t', -'pa_context_subscribe', 'pa_context_set_subscribe_callback', -'pa_context_play_sample_cb_t', 'pa_stream_connect_upload', -'pa_stream_finish_upload', 'pa_context_remove_sample', -'pa_context_play_sample', 'pa_context_play_sample_with_proplist', -'pa_strerror', 'pa_xmalloc', 'pa_xmalloc0', 'pa_xrealloc', 'pa_xfree', -'pa_xstrdup', 'pa_xstrndup', 'pa_xmemdup', '_pa_xnew_internal', -'_pa_xnew0_internal', '_pa_xnewdup_internal', '_pa_xrenew_internal', -'pa_utf8_valid', 'pa_ascii_valid', 'pa_utf8_filter', 'pa_ascii_filter', -'pa_utf8_to_locale', 'pa_locale_to_utf8', 'pa_threaded_mainloop', -'pa_threaded_mainloop_new', 'pa_threaded_mainloop_free', -'pa_threaded_mainloop_start', 'pa_threaded_mainloop_stop', -'pa_threaded_mainloop_lock', 'pa_threaded_mainloop_unlock', -'pa_threaded_mainloop_wait', 'pa_threaded_mainloop_signal', -'pa_threaded_mainloop_accept', 'pa_threaded_mainloop_get_retval', -'pa_threaded_mainloop_get_api', 'pa_threaded_mainloop_in_thread', -'pa_threaded_mainloop_set_name', 'pa_mainloop', 'pa_mainloop_new', -'pa_mainloop_free', 'pa_mainloop_prepare', 'pa_mainloop_poll', -'pa_mainloop_dispatch', 'pa_mainloop_get_retval', 'pa_mainloop_iterate', -'pa_mainloop_run', 'pa_mainloop_get_api', 'pa_mainloop_quit', -'pa_mainloop_wakeup', 'pa_poll_func', 'pa_mainloop_set_poll_func', -'pa_signal_event', 'pa_signal_cb_t', 'pa_signal_destroy_cb_t', -'pa_signal_init', 'pa_signal_done', 'pa_signal_new', 'pa_signal_free', -'pa_signal_set_destroy', 'pa_get_user_name', 'pa_get_host_name', -'pa_get_fqdn', 'pa_get_home_dir', 'pa_get_binary_name', -'pa_path_get_filename', 'pa_msleep', 'pa_gettimeofday', 'pa_timeval_diff', -'pa_timeval_cmp', 'pa_timeval_age', 'pa_timeval_add', 'pa_timeval_sub', -'pa_timeval_store', 'pa_timeval_load'] + 'PA_MAJOR', 'PA_MINOR', 'PA_MICRO', 'PA_CHANNELS_MAX', 'PA_RATE_MAX', + 'pa_sample_format_t', 'PA_SAMPLE_U8', 'PA_SAMPLE_ALAW', 'PA_SAMPLE_ULAW', + 'PA_SAMPLE_S16LE', 'PA_SAMPLE_S16BE', 'PA_SAMPLE_FLOAT32LE', + 'PA_SAMPLE_FLOAT32BE', 'PA_SAMPLE_S32LE', 'PA_SAMPLE_S32BE', + 'PA_SAMPLE_S24LE', 'PA_SAMPLE_S24BE', 'PA_SAMPLE_S24_32LE', + 'PA_SAMPLE_S24_32BE', 'PA_SAMPLE_MAX', 'PA_SAMPLE_INVALID', 'pa_sample_spec', + 'pa_usec_t', 'pa_bytes_per_second', 'pa_frame_size', 'pa_sample_size', + 'pa_sample_size_of_format', 'pa_bytes_to_usec', 'pa_usec_to_bytes', + 'pa_sample_spec_init', 'pa_sample_format_valid', 'pa_sample_rate_valid', + 'pa_channels_valid', 'pa_sample_spec_valid', 'pa_sample_spec_equal', + 'pa_sample_format_to_string', 'pa_parse_sample_format', + 'PA_SAMPLE_SPEC_SNPRINT_MAX', 'pa_sample_spec_snprint', + 'PA_BYTES_SNPRINT_MAX', 'pa_bytes_snprint', 'pa_sample_format_is_le', + 'pa_sample_format_is_be', 'pa_context_state_t', 'PA_CONTEXT_UNCONNECTED', + 'PA_CONTEXT_CONNECTING', 'PA_CONTEXT_AUTHORIZING', 'PA_CONTEXT_SETTING_NAME', + 'PA_CONTEXT_READY', 'PA_CONTEXT_FAILED', 'PA_CONTEXT_TERMINATED', + 'pa_stream_state_t', 'PA_STREAM_UNCONNECTED', 'PA_STREAM_CREATING', + 'PA_STREAM_READY', 'PA_STREAM_FAILED', 'PA_STREAM_TERMINATED', + 'pa_operation_state_t', 'PA_OPERATION_RUNNING', 'PA_OPERATION_DONE', + 'PA_OPERATION_CANCELLED', 'pa_context_flags_t', 'PA_CONTEXT_NOFLAGS', + 'PA_CONTEXT_NOAUTOSPAWN', 'PA_CONTEXT_NOFAIL', 'pa_direction_t', + 'PA_DIRECTION_OUTPUT', 'PA_DIRECTION_INPUT', 'pa_device_type_t', + 'PA_DEVICE_TYPE_SINK', 'PA_DEVICE_TYPE_SOURCE', 'pa_stream_direction_t', + 'PA_STREAM_NODIRECTION', 'PA_STREAM_PLAYBACK', 'PA_STREAM_RECORD', + 'PA_STREAM_UPLOAD', 'pa_stream_flags_t', 'PA_STREAM_NOFLAGS', + 'PA_STREAM_START_CORKED', 'PA_STREAM_INTERPOLATE_TIMING', + 'PA_STREAM_NOT_MONOTONIC', 'PA_STREAM_AUTO_TIMING_UPDATE', + 'PA_STREAM_NO_REMAP_CHANNELS', 'PA_STREAM_NO_REMIX_CHANNELS', + 'PA_STREAM_FIX_FORMAT', 'PA_STREAM_FIX_RATE', 'PA_STREAM_FIX_CHANNELS', + 'PA_STREAM_DONT_MOVE', 'PA_STREAM_VARIABLE_RATE', 'PA_STREAM_PEAK_DETECT', + 'PA_STREAM_START_MUTED', 'PA_STREAM_ADJUST_LATENCY', + 'PA_STREAM_EARLY_REQUESTS', 'PA_STREAM_DONT_INHIBIT_AUTO_SUSPEND', + 'PA_STREAM_START_UNMUTED', 'PA_STREAM_FAIL_ON_SUSPEND', + 'PA_STREAM_RELATIVE_VOLUME', 'PA_STREAM_PASSTHROUGH', 'pa_buffer_attr', + 'pa_error_code_t', 'PA_OK', 'PA_ERR_ACCESS', 'PA_ERR_COMMAND', + 'PA_ERR_INVALID', 'PA_ERR_EXIST', 'PA_ERR_NOENTITY', + 'PA_ERR_CONNECTIONREFUSED', 'PA_ERR_PROTOCOL', 'PA_ERR_TIMEOUT', + 'PA_ERR_AUTHKEY', 'PA_ERR_INTERNAL', 'PA_ERR_CONNECTIONTERMINATED', + 'PA_ERR_KILLED', 'PA_ERR_INVALIDSERVER', 'PA_ERR_MODINITFAILED', + 'PA_ERR_BADSTATE', 'PA_ERR_NODATA', 'PA_ERR_VERSION', 'PA_ERR_TOOLARGE', + 'PA_ERR_NOTSUPPORTED', 'PA_ERR_UNKNOWN', 'PA_ERR_NOEXTENSION', + 'PA_ERR_OBSOLETE', 'PA_ERR_NOTIMPLEMENTED', 'PA_ERR_FORKED', 'PA_ERR_IO', + 'PA_ERR_BUSY', 'PA_ERR_MAX', 'pa_subscription_mask_t', + 'PA_SUBSCRIPTION_MASK_NULL', 'PA_SUBSCRIPTION_MASK_SINK', + 'PA_SUBSCRIPTION_MASK_SOURCE', 'PA_SUBSCRIPTION_MASK_SINK_INPUT', + 'PA_SUBSCRIPTION_MASK_SOURCE_OUTPUT', 'PA_SUBSCRIPTION_MASK_MODULE', + 'PA_SUBSCRIPTION_MASK_CLIENT', 'PA_SUBSCRIPTION_MASK_SAMPLE_CACHE', + 'PA_SUBSCRIPTION_MASK_SERVER', 'PA_SUBSCRIPTION_MASK_AUTOLOAD', + 'PA_SUBSCRIPTION_MASK_CARD', 'PA_SUBSCRIPTION_MASK_ALL', + 'pa_subscription_event_type_t', 'PA_SUBSCRIPTION_EVENT_SINK', + 'PA_SUBSCRIPTION_EVENT_SOURCE', 'PA_SUBSCRIPTION_EVENT_SINK_INPUT', + 'PA_SUBSCRIPTION_EVENT_SOURCE_OUTPUT', 'PA_SUBSCRIPTION_EVENT_MODULE', + 'PA_SUBSCRIPTION_EVENT_CLIENT', 'PA_SUBSCRIPTION_EVENT_SAMPLE_CACHE', + 'PA_SUBSCRIPTION_EVENT_SERVER', 'PA_SUBSCRIPTION_EVENT_AUTOLOAD', + 'PA_SUBSCRIPTION_EVENT_CARD', 'PA_SUBSCRIPTION_EVENT_FACILITY_MASK', + 'PA_SUBSCRIPTION_EVENT_NEW', 'PA_SUBSCRIPTION_EVENT_CHANGE', + 'PA_SUBSCRIPTION_EVENT_REMOVE', 'PA_SUBSCRIPTION_EVENT_TYPE_MASK', + 'pa_timing_info', 'pa_spawn_api', 'pa_seek_mode_t', 'PA_SEEK_RELATIVE', + 'PA_SEEK_ABSOLUTE', 'PA_SEEK_RELATIVE_ON_READ', 'PA_SEEK_RELATIVE_END', + 'pa_sink_flags_t', 'PA_SINK_NOFLAGS', 'PA_SINK_HW_VOLUME_CTRL', + 'PA_SINK_LATENCY', 'PA_SINK_HARDWARE', 'PA_SINK_NETWORK', + 'PA_SINK_HW_MUTE_CTRL', 'PA_SINK_DECIBEL_VOLUME', 'PA_SINK_FLAT_VOLUME', + 'PA_SINK_DYNAMIC_LATENCY', 'PA_SINK_SET_FORMATS', 'pa_sink_state_t', + 'PA_SINK_INVALID_STATE', 'PA_SINK_RUNNING', 'PA_SINK_IDLE', + 'PA_SINK_SUSPENDED', 'PA_SINK_INIT', 'PA_SINK_UNLINKED', 'pa_source_flags_t', + 'PA_SOURCE_NOFLAGS', 'PA_SOURCE_HW_VOLUME_CTRL', 'PA_SOURCE_LATENCY', + 'PA_SOURCE_HARDWARE', 'PA_SOURCE_NETWORK', 'PA_SOURCE_HW_MUTE_CTRL', + 'PA_SOURCE_DECIBEL_VOLUME', 'PA_SOURCE_DYNAMIC_LATENCY', + 'PA_SOURCE_FLAT_VOLUME', 'pa_source_state_t', 'PA_SOURCE_INVALID_STATE', + 'PA_SOURCE_RUNNING', 'PA_SOURCE_IDLE', 'PA_SOURCE_SUSPENDED', + 'PA_SOURCE_INIT', 'PA_SOURCE_UNLINKED', 'pa_free_cb_t', 'pa_port_available_t', + 'PA_PORT_AVAILABLE_UNKNOWN', 'PA_PORT_AVAILABLE_NO', 'PA_PORT_AVAILABLE_YES', + 'pa_mainloop_api', 'pa_io_event_flags_t', 'PA_IO_EVENT_NULL', + 'PA_IO_EVENT_INPUT', 'PA_IO_EVENT_OUTPUT', 'PA_IO_EVENT_HANGUP', + 'PA_IO_EVENT_ERROR', 'pa_io_event', 'pa_io_event_cb_t', + 'pa_io_event_destroy_cb_t', 'pa_time_event', 'pa_time_event_cb_t', + 'pa_time_event_destroy_cb_t', 'pa_defer_event', 'pa_defer_event_cb_t', + 'pa_defer_event_destroy_cb_t', 'pa_mainloop_api_once', + 'pa_channel_position_t', 'PA_CHANNEL_POSITION_INVALID', + 'PA_CHANNEL_POSITION_MONO', 'PA_CHANNEL_POSITION_FRONT_LEFT', + 'PA_CHANNEL_POSITION_FRONT_RIGHT', 'PA_CHANNEL_POSITION_FRONT_CENTER', + 'PA_CHANNEL_POSITION_LEFT', 'PA_CHANNEL_POSITION_RIGHT', + 'PA_CHANNEL_POSITION_CENTER', 'PA_CHANNEL_POSITION_REAR_CENTER', + 'PA_CHANNEL_POSITION_REAR_LEFT', 'PA_CHANNEL_POSITION_REAR_RIGHT', + 'PA_CHANNEL_POSITION_LFE', 'PA_CHANNEL_POSITION_SUBWOOFER', + 'PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER', + 'PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER', 'PA_CHANNEL_POSITION_SIDE_LEFT', + 'PA_CHANNEL_POSITION_SIDE_RIGHT', 'PA_CHANNEL_POSITION_AUX0', + 'PA_CHANNEL_POSITION_AUX1', 'PA_CHANNEL_POSITION_AUX2', + 'PA_CHANNEL_POSITION_AUX3', 'PA_CHANNEL_POSITION_AUX4', + 'PA_CHANNEL_POSITION_AUX5', 'PA_CHANNEL_POSITION_AUX6', + 'PA_CHANNEL_POSITION_AUX7', 'PA_CHANNEL_POSITION_AUX8', + 'PA_CHANNEL_POSITION_AUX9', 'PA_CHANNEL_POSITION_AUX10', + 'PA_CHANNEL_POSITION_AUX11', 'PA_CHANNEL_POSITION_AUX12', + 'PA_CHANNEL_POSITION_AUX13', 'PA_CHANNEL_POSITION_AUX14', + 'PA_CHANNEL_POSITION_AUX15', 'PA_CHANNEL_POSITION_AUX16', + 'PA_CHANNEL_POSITION_AUX17', 'PA_CHANNEL_POSITION_AUX18', + 'PA_CHANNEL_POSITION_AUX19', 'PA_CHANNEL_POSITION_AUX20', + 'PA_CHANNEL_POSITION_AUX21', 'PA_CHANNEL_POSITION_AUX22', + 'PA_CHANNEL_POSITION_AUX23', 'PA_CHANNEL_POSITION_AUX24', + 'PA_CHANNEL_POSITION_AUX25', 'PA_CHANNEL_POSITION_AUX26', + 'PA_CHANNEL_POSITION_AUX27', 'PA_CHANNEL_POSITION_AUX28', + 'PA_CHANNEL_POSITION_AUX29', 'PA_CHANNEL_POSITION_AUX30', + 'PA_CHANNEL_POSITION_AUX31', 'PA_CHANNEL_POSITION_TOP_CENTER', + 'PA_CHANNEL_POSITION_TOP_FRONT_LEFT', 'PA_CHANNEL_POSITION_TOP_FRONT_RIGHT', + 'PA_CHANNEL_POSITION_TOP_FRONT_CENTER', 'PA_CHANNEL_POSITION_TOP_REAR_LEFT', + 'PA_CHANNEL_POSITION_TOP_REAR_RIGHT', 'PA_CHANNEL_POSITION_TOP_REAR_CENTER', + 'PA_CHANNEL_POSITION_MAX', 'pa_channel_position_mask_t', + 'pa_channel_map_def_t', 'PA_CHANNEL_MAP_AIFF', 'PA_CHANNEL_MAP_ALSA', + 'PA_CHANNEL_MAP_AUX', 'PA_CHANNEL_MAP_WAVEEX', 'PA_CHANNEL_MAP_OSS', + 'PA_CHANNEL_MAP_DEF_MAX', 'PA_CHANNEL_MAP_DEFAULT', 'pa_channel_map', + 'pa_channel_map_init', 'pa_channel_map_init_mono', + 'pa_channel_map_init_stereo', 'pa_channel_map_init_auto', + 'pa_channel_map_init_extend', 'pa_channel_position_to_string', + 'pa_channel_position_from_string', 'pa_channel_position_to_pretty_string', + 'PA_CHANNEL_MAP_SNPRINT_MAX', 'pa_channel_map_snprint', + 'pa_channel_map_parse', 'pa_channel_map_equal', 'pa_channel_map_valid', + 'pa_channel_map_compatible', 'pa_channel_map_superset', + 'pa_channel_map_can_balance', 'pa_channel_map_can_fade', + 'pa_channel_map_to_name', 'pa_channel_map_to_pretty_name', + 'pa_channel_map_has_position', 'pa_channel_map_mask', 'pa_operation', + 'pa_operation_notify_cb_t', 'pa_operation_ref', 'pa_operation_unref', + 'pa_operation_cancel', 'pa_operation_get_state', + 'pa_operation_set_state_callback', 'pa_context', 'pa_context_notify_cb_t', + 'pa_context_success_cb_t', 'pa_context_event_cb_t', 'pa_context_new', + 'pa_context_new_with_proplist', 'pa_context_unref', 'pa_context_ref', + 'pa_context_set_state_callback', 'pa_context_set_event_callback', + 'pa_context_errno', 'pa_context_is_pending', 'pa_context_get_state', + 'pa_context_connect', 'pa_context_disconnect', 'pa_context_drain', + 'pa_context_exit_daemon', 'pa_context_set_default_sink', + 'pa_context_set_default_source', 'pa_context_is_local', 'pa_context_set_name', + 'pa_context_get_server', 'pa_context_get_protocol_version', + 'pa_context_get_server_protocol_version', 'PA_UPDATE_SET', 'PA_UPDATE_MERGE', + 'PA_UPDATE_REPLACE', 'pa_context_proplist_update', + 'pa_context_proplist_remove', 'pa_context_get_index', 'pa_context_rttime_new', + 'pa_context_rttime_restart', 'pa_context_get_tile_size', + 'pa_context_load_cookie_from_file', 'pa_volume_t', 'pa_cvolume', + 'pa_cvolume_equal', 'pa_cvolume_init', 'pa_cvolume_set', + 'PA_CVOLUME_SNPRINT_MAX', 'pa_cvolume_snprint', + 'PA_SW_CVOLUME_SNPRINT_DB_MAX', 'pa_sw_cvolume_snprint_dB', + 'PA_CVOLUME_SNPRINT_VERBOSE_MAX', 'pa_cvolume_snprint_verbose', + 'PA_VOLUME_SNPRINT_MAX', 'pa_volume_snprint', 'PA_SW_VOLUME_SNPRINT_DB_MAX', + 'pa_sw_volume_snprint_dB', 'PA_VOLUME_SNPRINT_VERBOSE_MAX', + 'pa_volume_snprint_verbose', 'pa_cvolume_avg', 'pa_cvolume_avg_mask', + 'pa_cvolume_max', 'pa_cvolume_max_mask', 'pa_cvolume_min', + 'pa_cvolume_min_mask', 'pa_cvolume_valid', 'pa_cvolume_channels_equal_to', + 'pa_sw_volume_multiply', 'pa_sw_cvolume_multiply', + 'pa_sw_cvolume_multiply_scalar', 'pa_sw_volume_divide', + 'pa_sw_cvolume_divide', 'pa_sw_cvolume_divide_scalar', 'pa_sw_volume_from_dB', + 'pa_sw_volume_to_dB', 'pa_sw_volume_from_linear', 'pa_sw_volume_to_linear', + 'pa_cvolume_remap', 'pa_cvolume_compatible', + 'pa_cvolume_compatible_with_channel_map', 'pa_cvolume_get_balance', + 'pa_cvolume_set_balance', 'pa_cvolume_get_fade', 'pa_cvolume_set_fade', + 'pa_cvolume_scale', 'pa_cvolume_scale_mask', 'pa_cvolume_set_position', + 'pa_cvolume_get_position', 'pa_cvolume_merge', 'pa_cvolume_inc_clamp', + 'pa_cvolume_inc', 'pa_cvolume_dec', 'pa_stream', 'pa_stream_success_cb_t', + 'pa_stream_request_cb_t', 'pa_stream_notify_cb_t', 'pa_stream_event_cb_t', + 'pa_stream_new', 'pa_stream_new_with_proplist', 'PA_ENCODING_ANY', + 'PA_ENCODING_PCM', 'PA_ENCODING_AC3_IEC61937', 'PA_ENCODING_EAC3_IEC61937', + 'PA_ENCODING_MPEG_IEC61937', 'PA_ENCODING_DTS_IEC61937', + 'PA_ENCODING_MPEG2_AAC_IEC61937', 'PA_ENCODING_MAX', 'PA_ENCODING_INVALID', + 'pa_stream_new_extended', 'pa_stream_unref', 'pa_stream_ref', + 'pa_stream_get_state', 'pa_stream_get_context', 'pa_stream_get_index', + 'pa_stream_get_device_index', 'pa_stream_get_device_name', + 'pa_stream_is_suspended', 'pa_stream_is_corked', 'pa_stream_connect_playback', + 'pa_stream_connect_record', 'pa_stream_disconnect', 'pa_stream_begin_write', + 'pa_stream_cancel_write', 'pa_stream_write', 'pa_stream_write_ext_free', + 'pa_stream_peek', 'pa_stream_drop', 'pa_stream_writable_size', + 'pa_stream_readable_size', 'pa_stream_drain', 'pa_stream_update_timing_info', + 'pa_stream_set_state_callback', 'pa_stream_set_write_callback', + 'pa_stream_set_read_callback', 'pa_stream_set_overflow_callback', + 'pa_stream_get_underflow_index', 'pa_stream_set_underflow_callback', + 'pa_stream_set_started_callback', 'pa_stream_set_latency_update_callback', + 'pa_stream_set_moved_callback', 'pa_stream_set_suspended_callback', + 'pa_stream_set_event_callback', 'pa_stream_set_buffer_attr_callback', + 'pa_stream_cork', 'pa_stream_flush', 'pa_stream_prebuf', 'pa_stream_trigger', + 'pa_stream_set_name', 'pa_stream_get_time', 'pa_stream_get_latency', + 'pa_stream_get_timing_info', 'pa_stream_get_sample_spec', + 'pa_stream_get_channel_map', 'pa_stream_get_format_info', + 'pa_stream_get_buffer_attr', 'pa_stream_set_buffer_attr', + 'pa_stream_update_sample_rate', 'pa_stream_proplist_update', + 'pa_stream_proplist_remove', 'pa_stream_set_monitor_stream', + 'pa_stream_get_monitor_stream', 'pa_sink_port_info', 'pa_sink_info', + 'pa_sink_info_cb_t', 'pa_context_get_sink_info_by_name', + 'pa_context_get_sink_info_by_index', 'pa_context_get_sink_info_list', + 'pa_context_set_sink_volume_by_index', 'pa_context_set_sink_volume_by_name', + 'pa_context_set_sink_mute_by_index', 'pa_context_set_sink_mute_by_name', + 'pa_context_suspend_sink_by_name', 'pa_context_suspend_sink_by_index', + 'pa_context_set_sink_port_by_index', 'pa_context_set_sink_port_by_name', + 'pa_source_port_info', 'pa_source_info', 'pa_source_info_cb_t', + 'pa_context_get_source_info_by_name', 'pa_context_get_source_info_by_index', + 'pa_context_get_source_info_list', 'pa_context_set_source_volume_by_index', + 'pa_context_set_source_volume_by_name', 'pa_context_set_source_mute_by_index', + 'pa_context_set_source_mute_by_name', 'pa_context_suspend_source_by_name', + 'pa_context_suspend_source_by_index', 'pa_context_set_source_port_by_index', + 'pa_context_set_source_port_by_name', 'pa_server_info', 'pa_server_info_cb_t', + 'pa_context_get_server_info', 'pa_module_info', 'pa_module_info_cb_t', + 'pa_context_get_module_info', 'pa_context_get_module_info_list', + 'pa_context_index_cb_t', 'pa_context_load_module', 'pa_context_unload_module', + 'pa_client_info', 'pa_client_info_cb_t', 'pa_context_get_client_info', + 'pa_context_get_client_info_list', 'pa_context_kill_client', + 'pa_card_profile_info', 'pa_card_profile_info2', 'pa_card_port_info', + 'pa_card_info', 'pa_card_info_cb_t', 'pa_context_get_card_info_by_index', + 'pa_context_get_card_info_by_name', 'pa_context_get_card_info_list', + 'pa_context_set_card_profile_by_index', 'pa_context_set_card_profile_by_name', + 'pa_context_set_port_latency_offset', 'pa_sink_input_info', + 'pa_sink_input_info_cb_t', 'pa_context_get_sink_input_info', + 'pa_context_get_sink_input_info_list', 'pa_context_move_sink_input_by_name', + 'pa_context_move_sink_input_by_index', 'pa_context_set_sink_input_volume', + 'pa_context_set_sink_input_mute', 'pa_context_kill_sink_input', + 'pa_source_output_info', 'pa_source_output_info_cb_t', + 'pa_context_get_source_output_info', 'pa_context_get_source_output_info_list', + 'pa_context_move_source_output_by_name', + 'pa_context_move_source_output_by_index', + 'pa_context_set_source_output_volume', 'pa_context_set_source_output_mute', + 'pa_context_kill_source_output', 'pa_stat_info', 'pa_stat_info_cb_t', + 'pa_context_stat', 'pa_sample_info', 'pa_sample_info_cb_t', + 'pa_context_get_sample_info_by_name', 'pa_context_get_sample_info_by_index', + 'pa_context_get_sample_info_list', 'pa_autoload_type_t', 'PA_AUTOLOAD_SINK', + 'PA_AUTOLOAD_SOURCE', 'pa_autoload_info', 'pa_autoload_info_cb_t', + 'pa_context_get_autoload_info_by_name', + 'pa_context_get_autoload_info_by_index', 'pa_context_get_autoload_info_list', + 'pa_context_add_autoload', 'pa_context_remove_autoload_by_name', + 'pa_context_remove_autoload_by_index', 'pa_context_subscribe_cb_t', + 'pa_context_subscribe', 'pa_context_set_subscribe_callback', + 'pa_context_play_sample_cb_t', 'pa_stream_connect_upload', + 'pa_stream_finish_upload', 'pa_context_remove_sample', + 'pa_context_play_sample', 'pa_context_play_sample_with_proplist', + 'pa_strerror', 'pa_xmalloc', 'pa_xmalloc0', 'pa_xrealloc', 'pa_xfree', + 'pa_xstrdup', 'pa_xstrndup', 'pa_xmemdup', '_pa_xnew_internal', + '_pa_xnew0_internal', '_pa_xnewdup_internal', '_pa_xrenew_internal', + 'pa_utf8_valid', 'pa_ascii_valid', 'pa_utf8_filter', 'pa_ascii_filter', + 'pa_utf8_to_locale', 'pa_locale_to_utf8', 'pa_threaded_mainloop', + 'pa_threaded_mainloop_new', 'pa_threaded_mainloop_free', + 'pa_threaded_mainloop_start', 'pa_threaded_mainloop_stop', + 'pa_threaded_mainloop_lock', 'pa_threaded_mainloop_unlock', + 'pa_threaded_mainloop_wait', 'pa_threaded_mainloop_signal', + 'pa_threaded_mainloop_accept', 'pa_threaded_mainloop_get_retval', + 'pa_threaded_mainloop_get_api', 'pa_threaded_mainloop_in_thread', + 'pa_threaded_mainloop_set_name', 'pa_mainloop', 'pa_mainloop_new', + 'pa_mainloop_free', 'pa_mainloop_prepare', 'pa_mainloop_poll', + 'pa_mainloop_dispatch', 'pa_mainloop_get_retval', 'pa_mainloop_iterate', + 'pa_mainloop_run', 'pa_mainloop_get_api', 'pa_mainloop_quit', + 'pa_mainloop_wakeup', 'pa_poll_func', 'pa_mainloop_set_poll_func', + 'pa_signal_event', 'pa_signal_cb_t', 'pa_signal_destroy_cb_t', + 'pa_signal_init', 'pa_signal_done', 'pa_signal_new', 'pa_signal_free', + 'pa_signal_set_destroy', 'pa_get_user_name', 'pa_get_host_name', + 'pa_get_fqdn', 'pa_get_home_dir', 'pa_get_binary_name', + 'pa_path_get_filename', 'pa_msleep', 'pa_gettimeofday', 'pa_timeval_diff', + 'pa_timeval_cmp', 'pa_timeval_age', 'pa_timeval_add', 'pa_timeval_sub', + 'pa_timeval_store', 'pa_timeval_load'] diff -Nru pyglet-1.4.10/pyglet/media/drivers/silent/adaptation.py pyglet-1.5.14/pyglet/media/drivers/silent/adaptation.py --- pyglet-1.4.10/pyglet/media/drivers/silent/adaptation.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/silent/adaptation.py 2020-11-16 14:49:52.000000000 +0000 @@ -0,0 +1,121 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer +from pyglet.media.drivers.listener import AbstractListener + + +class SilentAudioPlayer(AbstractAudioPlayer): + + def delete(self): + pass + + def play(self): + pass + + def stop(self): + pass + + def clear(self): + pass + + def get_write_size(self): + pass + + def write(self, audio_data, length): + pass + + def get_time(self): + return 0 + + def set_volume(self, volume): + pass + + def set_position(self, position): + pass + + def set_min_distance(self, min_distance): + pass + + def set_max_distance(self, max_distance): + pass + + def set_pitch(self, pitch): + pass + + def set_cone_orientation(self, cone_orientation): + pass + + def set_cone_inner_angle(self, cone_inner_angle): + pass + + def set_cone_outer_angle(self, cone_outer_angle): + pass + + def set_cone_outer_gain(self, cone_outer_gain): + pass + + def prefill_audio(self): + pass + + +class SilentDriver(AbstractAudioDriver): + + def create_audio_player(self, source, player): + return SilentAudioPlayer(source, player) + + def get_listener(self): + return SilentListener() + + def delete(self): + pass + + +class SilentListener(AbstractListener): + + def _set_volume(self, volume): + pass + + def _set_position(self, position): + pass + + def _set_forward_orientation(self, orientation): + pass + + def _set_up_orientation(self, orientation): + pass + + def _set_orientation(self): + pass diff -Nru pyglet-1.4.10/pyglet/media/drivers/silent/__init__.py pyglet-1.5.14/pyglet/media/drivers/silent/__init__.py --- pyglet-1.4.10/pyglet/media/drivers/silent/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/silent/__init__.py 2020-11-16 14:49:52.000000000 +0000 @@ -0,0 +1,43 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +from . import adaptation + + +def create_audio_driver(): + return adaptation.SilentDriver() + + +__all__ = ["create_audio_driver"] diff -Nru pyglet-1.4.10/pyglet/media/drivers/xaudio2/adaptation.py pyglet-1.5.14/pyglet/media/drivers/xaudio2/adaptation.py --- pyglet-1.4.10/pyglet/media/drivers/xaudio2/adaptation.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/xaudio2/adaptation.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,372 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +import math + +import pyglet +from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer +from pyglet.media.drivers.listener import AbstractListener +from pyglet.media.events import MediaEvent +from pyglet.util import debug_print +from . import interface + +_debug = debug_print('debug_media') + + +def _convert_coordinates(coordinates): + x, y, z = coordinates + return x, y, -z + + +class XAudio2AudioPlayer(AbstractAudioPlayer): + # Need to cache these because pyglet API allows update separately, but + # DSound requires both to be set at once. + _cone_inner_angle = 360 + _cone_outer_angle = 360 + + min_buffer_size = 9600 + + max_buffer_count = 3 # Max in queue at once, increasing may impact performance depending on buffer size. + + def __init__(self, driver, xa2_driver, source, player): + super(XAudio2AudioPlayer, self).__init__(source, player) + # We keep here a strong reference because the AudioDriver is anyway + # a singleton object which will only be deleted when the application + # shuts down. The AudioDriver does not keep a ref to the AudioPlayer. + self.driver = driver + self._xa2_driver = xa2_driver + + # If cleared, we need to check when it's done clearing. + self._clearing = False + + # If deleted, we need to make sure it's done deleting. + self._deleted = False + + # Desired play state (may be actually paused due to underrun -- not + # implemented yet). + self._playing = False + + # Theoretical write and play cursors for an infinite buffer. play + # cursor is always <= write cursor (when equal, underrun is + # happening). + self._write_cursor = 0 + self._play_cursor = 0 + + # List of (play_cursor, MediaEvent), in sort order + self._events = [] + + # List of (cursor, timestamp), in sort order (cursor gives expiry + # place of the timestamp) + self._timestamps = [] + + # This will be True if the last buffer has already been submitted. + self.buffer_end_submitted = False + + self._buffers = [] + + self._xa2_source_voice = self._xa2_driver.get_source_voice(source, self) + + self._buffer_size = int(source.audio_format.sample_rate * 2) + + def on_driver_destroy(self): + self.stop() + self._xa2_source_voice = None + + def on_driver_reset(self): + self._xa2_source_voice = self._xa2_driver.get_source_voice(self.source, self) + + # Queue up any buffers that are still in queue but weren't deleted. This does not pickup where the last sample + # played, only where the last buffer was submitted. As such it's possible for audio to be replayed if buffer is + # large enough. + for cx2_buffer in self._buffers: + self._xa2_source_voice.submit_buffer(cx2_buffer) + + def __del__(self): + if self._xa2_source_voice: + self._xa2_source_voice = None + + def delete(self): + """Called from Player. Docs says to cleanup resources, but other drivers wait for GC to do it?""" + if self._xa2_source_voice: + self._deleted = True + + def play(self): + assert _debug('XAudio2 play') + + if not self._playing: + if not self._clearing: + self._playing = True + self._xa2_source_voice.play() + + assert _debug('return XAudio2 play') + + def stop(self): + assert _debug('XAudio2 stop') + + if self._playing: + self._playing = False + self.buffer_end_submitted = False + self._xa2_source_voice.stop() + + assert _debug('return XAudio2 stop') + + def clear(self): + assert _debug('XAudio2 clear') + super(XAudio2AudioPlayer, self).clear() + self._play_cursor = 0 + self._write_cursor = 0 + self.buffer_end_submitted = False + self._clearing = True + self._deleted = False + self._xa2_source_voice.flush() + self._buffers.clear() + del self._events[:] + del self._timestamps[:] + + def _restart(self, dt): + """Prefill audio and attempt to replay audio.""" + if self._xa2_source_voice: + self.prefill_audio() + self.play() + + def refill_source_player(self): + """Obtains audio data from the source, puts it into a buffer to submit to the voice. + Unlike the other drivers this does not carve pieces of audio from the buffer and slowly + consume it. This submits the buffer retrieved from the decoder in it's entirety. + """ + buffers_queued = self._xa2_source_voice.buffers_queued + + # Free any buffers that have ended. + while len(self._buffers) > buffers_queued: + # Clean out any buffers that have played. + buffer = self._buffers.pop(0) + self._play_cursor += buffer.AudioBytes + del buffer # Does this remove AudioData within the buffer? Let GC remove or explicit remove? + + # We have to wait for all of the buffers we are clearing to end before we restart next source. + # When we reach 0, schedule restart. + if self._clearing: + if buffers_queued == 0: + self._clearing = False + pyglet.clock.schedule_once(self._restart, 0) + return + + if self._deleted: + if buffers_queued == 0: + self._deleted = False + self._xa2_driver.return_voice(self._xa2_source_voice) + return + + # Wait for the playback to hit 0 buffers before we eos. + if self.buffer_end_submitted: + if buffers_queued == 0: + self._xa2_source_voice.stop() + MediaEvent(0, "on_eos")._sync_dispatch_to_player(self.player) + else: + current_buffers = [] + while buffers_queued < self.max_buffer_count: + audio_data = self.source.get_audio_data(self._buffer_size, 0.0) + if audio_data: + assert _debug( + 'Xaudio2: audio data - length: {}, duration: {}, buffer size: {}'.format(audio_data.length, + audio_data.duration, + self._buffer_size)) + + if audio_data.length == 0: # Sometimes audio data has 0 length at the front? + continue + + x2_buffer = self._xa2_driver.create_buffer(audio_data) + + current_buffers.append(x2_buffer) + + self._write_cursor += x2_buffer.AudioBytes # We've pushed this many bytes into the source player. + + self._add_audiodata_events(audio_data) + self._add_audiodata_timestamp(audio_data) + + buffers_queued += 1 + else: + # End of audio data, set last packet as end. + self.buffer_end_submitted = True + break + + # We submit the buffers here, just in-case the end of stream was found. + for cx2_buffer in current_buffers: + self._xa2_source_voice.submit_buffer(cx2_buffer) + + # Store buffers temporarily, otherwise they get GC'd. + self._buffers.extend(current_buffers) + + self._dispatch_pending_events() + + def _dispatch_new_event(self, event_name): + MediaEvent(0, event_name)._sync_dispatch_to_player(self.player) + + def _add_audiodata_events(self, audio_data): + for event in audio_data.events: + event_cursor = self._write_cursor + event.timestamp * \ + self.source.audio_format.bytes_per_second + assert _debug('Adding event', event, 'at', event_cursor) + self._events.append((event_cursor, event)) + + def _add_audiodata_timestamp(self, audio_data): + ts_cursor = self._write_cursor + audio_data.length + self._timestamps.append( + (ts_cursor, audio_data.timestamp + audio_data.duration)) + + def _dispatch_pending_events(self): + pending_events = [] + while self._events and self._events[0][0] <= self._play_cursor: + _, event = self._events.pop(0) + pending_events.append(event) + + assert _debug('Dispatching pending events: {}'.format(pending_events)) + assert _debug('Remaining events: {}'.format(self._events)) + + for event in pending_events: + event._sync_dispatch_to_player(self.player) + + def _cleanup_timestamps(self): + while self._timestamps and self._timestamps[0][0] < self._play_cursor: + del self._timestamps[0] + + def get_time(self): + self.update_play_cursor() + if self._timestamps: + cursor, ts = self._timestamps[0] + result = ts + (self._play_cursor - cursor) / float(self.source.audio_format.bytes_per_second) + else: + result = None + + return result + + def set_volume(self, volume): + self._xa2_source_voice.volume = volume + + def set_position(self, position): + if self._xa2_source_voice.is_emitter: + self._xa2_source_voice.position = _convert_coordinates(position) + + def set_min_distance(self, min_distance): + """Not a true min distance, but similar effect. Changes CurveDistanceScaler default is 1.""" + if self._xa2_source_voice.is_emitter: + self._xa2_source_voice.distance_scaler = min_distance + + def set_max_distance(self, max_distance): + """No such thing built into xaudio2""" + return + + def set_pitch(self, pitch): + self._xa2_source_voice.frequency = pitch + + def set_cone_orientation(self, cone_orientation): + if self._xa2_source_voice.is_emitter: + self._xa2_source_voice.cone_orientation = _convert_coordinates(cone_orientation) + + def set_cone_inner_angle(self, cone_inner_angle): + if self._xa2_source_voice.is_emitter: + self._cone_inner_angle = int(cone_inner_angle) + self._set_cone_angles() + + def set_cone_outer_angle(self, cone_outer_angle): + if self._xa2_source_voice.is_emitter: + self._cone_outer_angle = int(cone_outer_angle) + self._set_cone_angles() + + def _set_cone_angles(self): + inner = min(self._cone_inner_angle, self._cone_outer_angle) + outer = max(self._cone_inner_angle, self._cone_outer_angle) + self._xa2_source_voice.set_cone_angles(math.radians(inner), math.radians(outer)) + + def set_cone_outer_gain(self, cone_outer_gain): + if self._xa2_source_voice.is_emitter: + self._xa2_source_voice.cone_outside_volume = cone_outer_gain + + def prefill_audio(self): + self.refill_source_player() + + +class XAudio2Driver(AbstractAudioDriver): + def __init__(self): + self._xa2_driver = interface.XAudio2Driver() + self._xa2_listener = self._xa2_driver.create_listener() + + assert self._xa2_driver is not None + assert self._xa2_listener is not None + + def __del__(self): + self.delete() + + def get_performance(self): + assert self._xa2_driver is not None + return self._xa2_driver.get_performance() + + def create_audio_player(self, source, player): + assert self._xa2_driver is not None + return XAudio2AudioPlayer(self, self._xa2_driver, source, player) + + def get_listener(self): + assert self._xa2_driver is not None + assert self._xa2_listener is not None + return XAudio2Listener(self._xa2_listener, self._xa2_driver) + + def delete(self): + self._xa2_listener = None + + +class XAudio2Listener(AbstractListener): + def __init__(self, xa2_listener, xa2_driver): + self._xa2_listener = xa2_listener + self._xa2_driver = xa2_driver + + def _set_volume(self, volume): + self._volume = volume + self._xa2_driver.volume = volume + + def _set_position(self, position): + self._position = position + self._xa2_listener.position = _convert_coordinates(position) + + def _set_forward_orientation(self, orientation): + self._forward_orientation = orientation + self._set_orientation() + + def _set_up_orientation(self, orientation): + self._up_orientation = orientation + self._set_orientation() + + def _set_orientation(self): + self._xa2_listener.orientation = _convert_coordinates(self._forward_orientation) + _convert_coordinates( + self._up_orientation) diff -Nru pyglet-1.4.10/pyglet/media/drivers/xaudio2/__init__.py pyglet-1.5.14/pyglet/media/drivers/xaudio2/__init__.py --- pyglet-1.4.10/pyglet/media/drivers/xaudio2/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/xaudio2/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,43 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +from . import adaptation + + +def create_audio_driver(): + return adaptation.XAudio2Driver() + + +__all__ = ["create_audio_driver"] diff -Nru pyglet-1.4.10/pyglet/media/drivers/xaudio2/interface.py pyglet-1.5.14/pyglet/media/drivers/xaudio2/interface.py --- pyglet-1.4.10/pyglet/media/drivers/xaudio2/interface.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/xaudio2/interface.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,612 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- +import weakref +from collections import namedtuple, defaultdict + +import pyglet +from pyglet.libs.win32.types import * +from pyglet.util import debug_print +from pyglet.media.devices import get_audio_device_manager +from . import lib_xaudio2 as lib + +_debug = debug_print('debug_media') + + +class XAudio2Driver: + # Specifies if positional audio should be used. Can be enabled later, but not disabled. + allow_3d = True + + # Which processor to use. (#1 by default) + processor = lib.XAUDIO2_DEFAULT_PROCESSOR + + # Which stream classification Windows uses on this driver. + category = lib.AudioCategory_GameEffects + + # If the driver errors or disappears, it will attempt to restart the engine. + restart_on_error = True + + # Max Frequency a voice can have. Setting this higher/lower will increase/decrease memory allocation. + max_frequency_ratio = 2.0 + + def __init__(self): + """Creates an XAudio2 master voice and sets up 3D audio if specified. This attaches to the default audio + device and will create a virtual audio endpoint that changes with the system. It will not recover if a + critical error is encountered such as no more audio devices are present. + """ + assert _debug('Constructing XAudio2Driver') + self._listener = None + self._xaudio2 = None + self._dead = False + + self._emitting_voices = [] # Contains all of the emitting source voices. + self._voice_pool = defaultdict(list) + self._in_use = [] # All voices currently in use. + + self._players = [] # Only used for resetting/restoring xaudio2. Store players to callback. + + if self.restart_on_error: + audio_devices = get_audio_device_manager() + if audio_devices: + assert _debug('Audio device instance found.') + audio_devices.push_handlers(self) + + if audio_devices.get_default_output() is None: + raise ImportError("No default audio device found, can not create driver.") + + pyglet.clock.schedule_interval_soft(self._check_state, 0.5) + + self._create_xa2() + + def _check_state(self, dt): + """Hack/workaround, you cannot shutdown/create XA2 within a COM callback, set a schedule to check state.""" + if self._dead is True: + if self._xaudio2: + self._shutdown_xaudio2() + else: + if not self._xaudio2: + self._create_xa2() + # Notify all active it's reset. + for player in self._players: + player.dispatch_event('on_driver_reset') + + self._players.clear() + + def on_default_changed(self, device): + """Callback derived from the Audio Devices to help us determine when the system no longer has output.""" + if device is None: + assert _debug('Error: Default audio device was removed or went missing.') + self._dead = True + else: + if self._dead: + assert _debug('Warning: Default audio device added after going missing.') + self._dead = False + + def _create_xa2(self, device_id=None): + self._xaudio2 = lib.IXAudio2() + lib.XAudio2Create(ctypes.byref(self._xaudio2), 0, self.processor) + + if _debug: + # Debug messages are found in Windows Event Viewer, you must enable event logging: + # Applications and Services -> Microsoft -> Windows -> Xaudio2 -> Debug Logging. + # Right click -> Enable Logs + debug = lib.XAUDIO2_DEBUG_CONFIGURATION() + debug.LogThreadID = True + debug.TraceMask = lib.XAUDIO2_LOG_ERRORS | lib.XAUDIO2_LOG_WARNINGS + debug.BreakMask = lib.XAUDIO2_LOG_WARNINGS + + self._xaudio2.SetDebugConfiguration(ctypes.byref(debug), None) + + self._master_voice = lib.IXAudio2MasteringVoice() + self._xaudio2.CreateMasteringVoice(byref(self._master_voice), + lib.XAUDIO2_DEFAULT_CHANNELS, + lib.XAUDIO2_DEFAULT_SAMPLERATE, + 0, device_id, None, self.category) + + if self.allow_3d: + self.enable_3d() + + @property + def active_voices(self): + return self._in_use + + @property + def pooled_voices(self): + return [voice for voices in self._voice_pool.values() for voice in voices] + + @property + def all_voices(self): + """All pooled and active voices.""" + return self.active_voices + self.all_voices + + def clear_pool(self): + """Destroy and then clear the pool of voices""" + for voice in self.pooled_voices: + voice.destroy() + + for voice_key in self._voice_pool: + self._voice_pool[voice_key].clear() + + def clear_active(self): + """Destroy and then clear all active voices""" + for voice in self._in_use: + voice.destroy() + + self._in_use.clear() + + def set_device(self, device): + """Attach XA2 with a specific device rather than the virtual device.""" + self._shutdown_xaudio2() + self._create_xa2(device.id) + + # Notify all active players it's reset.. + for player in self._players: + player.dispatch_event('on_driver_reset') + + self._players.clear() + + def _shutdown_xaudio2(self): + """Stops and destroys all active voices, then destroys XA2 instance.""" + for voice in self.active_voices: + voice.player.on_driver_destroy() + self._players.append(voice.player.player) + + self._delete_driver() + + def _delete_driver(self): + if self._xaudio2: + # Stop 3d + if self.allow_3d: + pyglet.clock.unschedule(self._calculate_3d_sources) + + # Destroy all pooled voices as master will change. + self.clear_pool() + self.clear_active() + + self._xaudio2.StopEngine() + self._xaudio2.Release() + self._xaudio2 = None + + def enable_3d(self): + """Initializes the prerequisites for 3D positional audio and initializes with default DSP settings.""" + channel_mask = DWORD() + self._master_voice.GetChannelMask(byref(channel_mask)) + + self._x3d_handle = lib.X3DAUDIO_HANDLE() + lib.X3DAudioInitialize(channel_mask.value, lib.X3DAUDIO_SPEED_OF_SOUND, self._x3d_handle) + + self._mvoice_details = lib.XAUDIO2_VOICE_DETAILS() + self._master_voice.GetVoiceDetails(byref(self._mvoice_details)) + + matrix = (FLOAT * self._mvoice_details.InputChannels)() + self._dsp_settings = lib.X3DAUDIO_DSP_SETTINGS() + self._dsp_settings.SrcChannelCount = 1 + self._dsp_settings.DstChannelCount = self._mvoice_details.InputChannels + self._dsp_settings.pMatrixCoefficients = matrix + + pyglet.clock.schedule_interval_soft(self._calculate_3d_sources, 1 / 15.0) + + @property + def volume(self): + vol = c_float() + self._master_voice.GetVolume(ctypes.byref(vol)) + return vol.value + + @volume.setter + def volume(self, value): + """Sets global volume of the master voice.""" + self._master_voice.SetVolume(value, 0) + + def _calculate_3d_sources(self, dt): + """We calculate the 3d emitters and sources every 15 fps, committing everything after deferring all changes.""" + for source_voice in self._emitting_voices: + self.apply3d(source_voice) + + self._xaudio2.CommitChanges(0) + + def _calculate3d(self, listener, emitter): + lib.X3DAudioCalculate( + self._x3d_handle, + listener, + emitter, + lib.default_dsp_calculation, + self._dsp_settings + ) + + def _apply3d(self, voice, commit): + """Calculates the output channels based on the listener and emitter and default DSP settings. + Commit determines if the settings are applied immediately (0) or committed at once through the xaudio driver. + """ + voice.SetOutputMatrix(self._master_voice, + 1, + self._mvoice_details.InputChannels, + self._dsp_settings.pMatrixCoefficients, + commit) + + voice.SetFrequencyRatio(self._dsp_settings.DopplerFactor, commit) + + def apply3d(self, source_voice, commit=1): + self._calculate3d(self._listener.listener, source_voice._emitter) + self._apply3d(source_voice._voice, commit) + + def __del__(self): + try: + self._delete_driver() + pyglet.clock.unschedule(self._check_state) + except AttributeError: + # Usually gets unloaded by default on app exit, but be safe. + pass + + def get_performance(self): + """Retrieve some basic XAudio2 performance data such as memory usage and source counts.""" + pf = lib.XAUDIO2_PERFORMANCE_DATA() + self._xaudio2.GetPerformanceData(ctypes.byref(pf)) + return pf + + def create_listener(self): + assert self._listener is None, "You can only create one listener." + self._listener = XAudio2Listener(self) + return self._listener + + def get_source_voice(self, source, player): + """ Get a source voice from the pool. Source voice creation can be slow to create/destroy. So pooling is + recommended. We pool based on audio channels as channels must be the same as well as frequency. + Source voice handles all of the audio playing and state for a single source.""" + voice_key = (source.audio_format.channels, source.audio_format.sample_size) + if len(self._voice_pool[voice_key]) > 0: + source_voice = self._voice_pool[voice_key].pop(0) + source_voice.acquired(player) + else: + source_voice = self._get_voice(source, player) + + if source_voice.is_emitter: + self._emitting_voices.append(source_voice) + + self._in_use.append(source_voice) + return source_voice + + def _create_new_voice(self, source, player): + """Has the driver create a new source voice for the source.""" + voice = lib.IXAudio2SourceVoice() + + wfx_format = self.create_wave_format(source.audio_format) + + callback = lib.XA2SourceCallback(player) + self._xaudio2.CreateSourceVoice(ctypes.byref(voice), + ctypes.byref(wfx_format), + 0, + self.max_frequency_ratio, + callback, + None, None) + return voice, callback + + def _get_voice(self, source, player): + """Creates a new source voice and puts it into XA2SourceVoice high level wrap.""" + voice, callback = self._create_new_voice(source, player) + return XA2SourceVoice(voice, callback, source.audio_format) + + def return_voice(self, voice): + """Reset a voice and return it to the pool.""" + voice.reset() + voice_key = (voice.audio_format.channels, voice.audio_format.sample_size) + self._voice_pool[voice_key].append(voice) + + if voice.is_emitter: + self._emitting_voices.remove(voice) + + @staticmethod + def create_buffer(audio_data): + """Creates a XAUDIO2_BUFFER to be used with a source voice. + Audio data cannot be purged until the source voice has played it; doing so will cause glitches. + Furthermore, if the data is not in a string buffer, such as pure bytes, it must be converted.""" + if type(audio_data.data) == bytes: + data = (ctypes.c_char * audio_data.length)() + ctypes.memmove(data, audio_data.data, audio_data.length) + else: + data = audio_data.data + + buff = lib.XAUDIO2_BUFFER() + buff.AudioBytes = audio_data.length + buff.pAudioData = data + return buff + + @staticmethod + def create_wave_format(audio_format): + wfx = lib.WAVEFORMATEX() + wfx.wFormatTag = lib.WAVE_FORMAT_PCM + wfx.nChannels = audio_format.channels + wfx.nSamplesPerSec = audio_format.sample_rate + wfx.wBitsPerSample = audio_format.sample_size + wfx.nBlockAlign = wfx.wBitsPerSample * wfx.nChannels // 8 + wfx.nAvgBytesPerSec = wfx.nSamplesPerSec * wfx.nBlockAlign + return wfx + + +class XA2SourceVoice: + + def __init__(self, voice, callback, audio_format): + self._voice_state = lib.XAUDIO2_VOICE_STATE() # Used for buffer state, will be reused constantly. + self._voice = voice + self._callback = callback + + self.audio_format = audio_format + # If it's a mono source, then we can make it an emitter. + # In the future, non-mono source's can be supported as well. + if audio_format is not None and audio_format.channels == 1: + self._emitter = lib.X3DAUDIO_EMITTER() + self._emitter.ChannelCount = audio_format.channels + self._emitter.CurveDistanceScaler = 1.0 + + # Commented are already set by the Player class. + # Leaving for visibility on default values + cone = lib.X3DAUDIO_CONE() + # cone.InnerAngle = math.radians(360) + # cone.OuterAngle = math.radians(360) + cone.InnerVolume = 1.0 + # cone.OuterVolume = 1.0 + + self._emitter.pCone = pointer(cone) + self._emitter.pVolumeCurve = None + else: + self._emitter = None + + @property + def player(self): + """Returns the player class, stored within the callback.""" + return self._callback.xa2_player + + def delete(self): + self._emitter = None + self._voice.Stop(0, 0) + self._voice.FlushSourceBuffers() + self._voice = None + self._callback.xa2_player = None + + def __del__(self): + self.destroy() + + def destroy(self): + """Completely destroy the voice.""" + self._emitter = None + + if self._voice is not None: + try: + self._voice.Stop(0, 0) + self._voice.FlushSourceBuffers() + self._voice.DestroyVoice() + except TypeError: + pass + + self._voice = None + + self._callback = None + + def acquired(self, player): + """A voice has been reacquired, set the player for callback.""" + self._callback.xa2_player = player + + def reset(self): + """When a voice is returned to the pool, reset position on emitter.""" + if self._emitter is not None: + self.position = (0, 0, 0) + + self._voice.Stop(0, 0) + self._voice.FlushSourceBuffers() + self._callback.xa2_player = None + + @property + def buffers_queued(self): + """Get the amount of buffers in the current voice. Adding flag for no samples played is 3x faster.""" + self._voice.GetState(ctypes.byref(self._voice_state), lib.XAUDIO2_VOICE_NOSAMPLESPLAYED) + return self._voice_state.BuffersQueued + + @property + def volume(self): + vol = c_float() + self._voice.GetVolume(ctypes.byref(vol)) + return vol.value + + @volume.setter + def volume(self, value): + self._voice.SetVolume(value, 0) + + @property + def is_emitter(self): + return self._emitter is not None + + @property + def position(self): + if self.is_emitter: + return self._emitter.Position.x, self._emitter.Position.y, self._emitter.Position.z + else: + return 0, 0, 0 + + @position.setter + def position(self, position): + if self.is_emitter: + x, y, z = position + self._emitter.Position.x = x + self._emitter.Position.y = y + self._emitter.Position.z = z + + @property + def min_distance(self): + """Curve distance scaler that is used to scale normalized distance curves to user-defined world units, + and/or to exaggerate their effect.""" + if self.is_emitter: + return self._emitter.CurveDistanceScaler + else: + return 0 + + @min_distance.setter + def min_distance(self, value): + if self.is_emitter: + if self._emitter.CurveDistanceScaler != value: + self._emitter.CurveDistanceScaler = min(value, lib.FLT_MAX) + + @property + def frequency(self): + """The actual frequency ratio. If voice is 3d enabled, will be overwritten next apply3d cycle.""" + value = c_float() + self._voice.GetFrequencyRatio(byref(value)) + return value.value + + @frequency.setter + def frequency(self, value): + if self.frequency == value: + return + + self._voice.SetFrequencyRatio(value, 0) + + @property + def cone_orientation(self): + """The orientation of the sound emitter.""" + if self.is_emitter: + return self._emitter.OrientFront.x, self._emitter.OrientFront.y, self._emitter.OrientFront.z + else: + return 0, 0, 0 + + @cone_orientation.setter + def cone_orientation(self, value): + if self.is_emitter: + x, y, z = value + self._emitter.OrientFront.x = x + self._emitter.OrientFront.y = y + self._emitter.OrientFront.z = z + + _ConeAngles = namedtuple('_ConeAngles', ['inside', 'outside']) + + @property + def cone_angles(self): + """The inside and outside angles of the sound projection cone.""" + if self.is_emitter: + return self._ConeAngles(self._emitter.pCone.contents.InnerAngle, self._emitter.pCone.contents.OuterAngle) + else: + return self._ConeAngles(0, 0) + + def set_cone_angles(self, inside, outside): + """The inside and outside angles of the sound projection cone.""" + if self.is_emitter: + self._emitter.pCone.contents.InnerAngle = inside + self._emitter.pCone.contents.OuterAngle = outside + + @property + def cone_outside_volume(self): + """The volume scaler of the sound beyond the outer cone.""" + if self.is_emitter: + return self._emitter.pCone.contents.OuterVolume + else: + return 0 + + @cone_outside_volume.setter + def cone_outside_volume(self, value): + if self.is_emitter: + self._emitter.pCone.contents.OuterVolume = value + + @property + def cone_inside_volume(self): + """The volume scaler of the sound within the inner cone.""" + if self.is_emitter: + return self._emitter.pCone.contents.InnerVolume + else: + return 0 + + @cone_inside_volume.setter + def cone_inside_volume(self, value): + if self.is_emitter: + self._emitter.pCone.contents.InnerVolume = value + + def flush(self): + """Stop and removes all buffers already queued. OnBufferEnd is called for each.""" + self._voice.Stop(0, 0) + self._voice.FlushSourceBuffers() + + def play(self): + self._voice.Start(0, 0) + + def stop(self): + self._voice.Stop(0, 0) + + def submit_buffer(self, x2_buffer): + self._voice.SubmitSourceBuffer(ctypes.byref(x2_buffer), None) + + +class XAudio2Listener: + def __init__(self, driver): + self.xa2_driver = weakref.proxy(driver) + self.listener = lib.X3DAUDIO_LISTENER() + + # Default listener orientations for DirectSound/XAudio2: + # Front: (0, 0, 1), Up: (0, 1, 0) + self.listener.OrientFront.x = 0 + self.listener.OrientFront.y = 0 + self.listener.OrientFront.z = 1 + + self.listener.OrientTop.x = 0 + self.listener.OrientTop.y = 1 + self.listener.OrientTop.z = 0 + + def __del__(self): + self.delete() + + def delete(self): + self.listener = None + + @property + def position(self): + return self.listener.Position.x, self.listener.Position.y, self.listener.Position.z + + @position.setter + def position(self, value): + x, y, z = value + self.listener.Position.x = x + self.listener.Position.y = y + self.listener.Position.z = z + + @property + def orientation(self): + return self.listener.OrientFront.x, self.listener.OrientFront.y, self.listener.OrientFront.z, \ + self.listener.OrientTop.x, self.listener.OrientTop.y, self.listener.OrientTop.z + + @orientation.setter + def orientation(self, orientation): + front_x, front_y, front_z, top_x, top_y, top_z = orientation + + self.listener.OrientFront.x = front_x + self.listener.OrientFront.y = front_y + self.listener.OrientFront.z = front_z + + self.listener.OrientTop.x = top_x + self.listener.OrientTop.y = top_y + self.listener.OrientTop.z = top_z diff -Nru pyglet-1.4.10/pyglet/media/drivers/xaudio2/lib_xaudio2.py pyglet-1.5.14/pyglet/media/drivers/xaudio2/lib_xaudio2.py --- pyglet-1.4.10/pyglet/media/drivers/xaudio2/lib_xaudio2.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/drivers/xaudio2/lib_xaudio2.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,681 @@ +from pyglet.media.events import MediaEvent + +import pyglet +import ctypes +from pyglet.libs.win32.constants import * +from pyglet.libs.win32.types import * +from pyglet import com +import platform +import os +from pyglet.util import debug_print + +_debug = debug_print('debug_media') + + +def load_xaudio2(dll_name): + """This will attempt to load a version of XAudio2. Versions supported: 2.9, 2.8. + While Windows 8 ships with 2.8 and Windows 10 ships with version 2.9, it is possible to install 2.9 on 8/8.1. + """ + xaudio2 = dll_name + # System32 and SysWOW64 folders are opposite perception in Windows x64. + # System32 = x64 dll's | SysWOW64 = x86 dlls + # By default ctypes only seems to look in system32 regardless of Python architecture, which has x64 dlls. + if platform.architecture()[0] == '32bit': + if platform.machine().endswith('64'): # Machine is 64 bit, Python is 32 bit. + xaudio2 = os.path.join(os.environ['WINDIR'], 'SysWOW64', '{}.dll'.format(xaudio2)) + + xaudio2_lib = ctypes.windll.LoadLibrary(xaudio2) + + # Somehow x3d uses different calling structure than the rest of the DLL; Only affects 32 bit? Microsoft... + x3d_lib = ctypes.cdll.LoadLibrary(xaudio2) + return xaudio2_lib, x3d_lib + + +try: + xaudio2_lib, x3d_lib = load_xaudio2("xaudio2_9") +except OSError: + _debug("Could not load XAudio2.9 library") + try: + xaudio2_lib, x3d_lib = load_xaudio2("xaudio2_8") + except OSError: + _debug("Could not load XAudio2.8 library") + raise ImportError('Could not locate a supported XAudio2 library.') + + +UINT32 = c_uint32 +FLOAT32 = c_float + + +class XAUDIO2_DEBUG_CONFIGURATION(ctypes.Structure): + _fields_ = [ + ('TraceMask', UINT32), + ('BreakMask', UINT32), + ('LogThreadID', BOOL), + ('LogFileline', BOOL), + ('LogFunctionName', BOOL), + ('LogTiming', BOOL), + ] + + +class XAUDIO2_PERFORMANCE_DATA(ctypes.Structure): + _fields_ = [ + ('AudioCyclesSinceLastQuery', c_uint64), + ('TotalCyclesSinceLastQuery', c_uint64), + ('MinimumCyclesPerQuantum', UINT32), + ('MaximumCyclesPerQuantum', UINT32), + ('MemoryUsageInBytes', UINT32), + ('CurrentLatencyInSamples', UINT32), + ('GlitchesSinceEngineStarted', UINT32), + ('ActiveSourceVoiceCount', UINT32), + ('TotalSourceVoiceCount', UINT32), + ('ActiveSubmixVoiceCount', UINT32), + ('ActiveResamplerCount', UINT32), + ('ActiveMatrixMixCount', UINT32), + ('ActiveXmaSourceVoices', UINT32), + ('ActiveXmaStreams', UINT32), + ] + + def __repr__(self): + return "XAUDIO2PerformanceData(active_voices={}, total_voices={}, glitches={}, latency={} samples, memory_usage={} bytes)".format(self.ActiveSourceVoiceCount, self.TotalSourceVoiceCount, self.GlitchesSinceEngineStarted, self.CurrentLatencyInSamples, self.MemoryUsageInBytes) + + +class XAUDIO2_VOICE_SENDS(ctypes.Structure): + _fields_ = [ + ('SendCount', UINT32), + ('pSends', c_void_p), + ] + + +class XAUDIO2_BUFFER(ctypes.Structure): + _fields_ = [ + ('Flags', UINT32), + ('AudioBytes', UINT32), + ('pAudioData', POINTER(c_char)), + ('PlayBegin', UINT32), + ('PlayLength', UINT32), + ('LoopBegin', UINT32), + ('LoopLength', UINT32), + ('LoopCount', UINT32), + ('pContext', c_void_p), + ] + +class XAUDIO2_VOICE_STATE(ctypes.Structure): + _fields_ = [ + ('pCurrentBufferContext', c_void_p), + ('BuffersQueued', UINT32), + ('SamplesPlayed', UINT32) + ] + + def __repr__(self): + return "XAUDIO2_VOICE_STATE(BuffersQueued={0}, SamplesPlayed={1})".format(self.BuffersQueued, self.SamplesPlayed) + +class WAVEFORMATEX(ctypes.Structure): + _fields_ = [ + ('wFormatTag', WORD), + ('nChannels', WORD), + ('nSamplesPerSec', DWORD), + ('nAvgBytesPerSec', DWORD), + ('nBlockAlign', WORD), + ('wBitsPerSample', WORD), + ('cbSize', WORD), + ] + + def __repr__(self): + return 'WAVEFORMATEX(wFormatTag={}, nChannels={}, nSamplesPerSec={}, nAvgBytesPersec={}' \ + ', nBlockAlign={}, wBitsPerSample={}, cbSize={})'.format( + self.wFormatTag, self.nChannels, self.nSamplesPerSec, + self.nAvgBytesPerSec, self.nBlockAlign, self.wBitsPerSample, + self.cbSize) + +XAUDIO2_USE_DEFAULT_PROCESSOR = 0x00000000 # Win 10+ + +if WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER: + XAUDIO2_DEFAULT_PROCESSOR = XAUDIO2_USE_DEFAULT_PROCESSOR +else: + XAUDIO2_DEFAULT_PROCESSOR = 0x00000001 # Windows 8/8.1 + + +XAUDIO2_LOG_ERRORS = 0x0001 # For handled errors with serious effects. +XAUDIO2_LOG_WARNINGS = 0x0002 # For handled errors that may be recoverable. +XAUDIO2_LOG_INFO = 0x0004 # Informational chit-chat (e.g. state changes). +XAUDIO2_LOG_DETAIL = 0x0008 # More detailed chit-chat. +XAUDIO2_LOG_API_CALLS = 0x0010 # Public API function entries and exits. +XAUDIO2_LOG_FUNC_CALLS = 0x0020 # Internal function entries and exits. +XAUDIO2_LOG_TIMING = 0x0040 # Delays detected and other timing data. +XAUDIO2_LOG_LOCKS = 0x0080 # Usage of critical sections and mutexes. +XAUDIO2_LOG_MEMORY = 0x0100 # Memory heap usage information. +XAUDIO2_LOG_STREAMING = 0x1000 # Audio streaming information. + + +# Some XAUDIO2 global settings, most not used, but useful information +XAUDIO2_MAX_BUFFER_BYTES = 0x80000000 # Maximum bytes allowed in a source buffer +XAUDIO2_MAX_QUEUED_BUFFERS = 64 # Maximum buffers allowed in a voice queue +XAUDIO2_MAX_BUFFERS_SYSTEM = 2 # Maximum buffers allowed for system threads (Xbox 360 only) +XAUDIO2_MAX_AUDIO_CHANNELS = 64 # Maximum channels in an audio stream +XAUDIO2_MIN_SAMPLE_RATE = 1000 # Minimum audio sample rate supported +XAUDIO2_MAX_SAMPLE_RATE = 200000 # Maximum audio sample rate supported +XAUDIO2_MAX_VOLUME_LEVEL = 16777216.0 # Maximum acceptable volume level (2^24) +XAUDIO2_MIN_FREQ_RATIO = (1/1024.0) # Minimum SetFrequencyRatio argument +XAUDIO2_MAX_FREQ_RATIO = 1024.0 # Maximum MaxFrequencyRatio argument +XAUDIO2_DEFAULT_FREQ_RATIO = 2.0 # Default MaxFrequencyRatio argument +XAUDIO2_MAX_FILTER_ONEOVERQ = 1.5 # Maximum XAUDIO2_FILTER_PARAMETERS.OneOverQ +XAUDIO2_MAX_FILTER_FREQUENCY = 1.0 # Maximum XAUDIO2_FILTER_PARAMETERS.Frequency +XAUDIO2_MAX_LOOP_COUNT = 254 # Maximum non-infinite XAUDIO2_BUFFER.LoopCount +XAUDIO2_MAX_INSTANCES = 8 # Maximum simultaneous XAudio2 objects on Xbox 360 + + +XAUDIO2_FILTER_TYPE = UINT +LowPassFilter = 0 # Attenuates frequencies above the cutoff frequency (state-variable filter). +BandPassFilter = 1 # Attenuates frequencies outside a given range (state-variable filter). +HighPassFilter = 2 # Attenuates frequencies below the cutoff frequency (state-variable filter). +NotchFilter = 3 # Attenuates frequencies inside a given range (state-variable filter). +LowPassOnePoleFilter = 4 # Attenuates frequencies above the cutoff frequency (one-pole filter, XAUDIO2_FILTER_PARAMETERS.OneOverQ has no effect) +HighPassOnePoleFilter = 5 # Attenuates frequencies below the cutoff frequency (one-pole filter, XAUDIO2_FILTER_PARAMETERS.OneOverQ has no effect) + +XAUDIO2_NO_LOOP_REGION = 0 # Used in XAUDIO2_BUFFER.LoopCount +XAUDIO2_LOOP_INFINITE = 255 # Used in XAUDIO2_BUFFER.LoopCount +XAUDIO2_DEFAULT_CHANNELS = 0 # Used in CreateMasteringVoice +XAUDIO2_DEFAULT_SAMPLERATE = 0 # Used in CreateMasteringVoice + +WAVE_FORMAT_PCM = 1 + +XAUDIO2_DEBUG_ENGINE = 0x0001 # Used in XAudio2Create +XAUDIO2_VOICE_NOPITCH = 0x0002 # Used in IXAudio2::CreateSourceVoice +XAUDIO2_VOICE_NOSRC = 0x0004 # Used in IXAudio2::CreateSourceVoice +XAUDIO2_VOICE_USEFILTER = 0x0008 # Used in IXAudio2::CreateSource/SubmixVoice +XAUDIO2_PLAY_TAILS = 0x0020 # Used in IXAudio2SourceVoice::Stop +XAUDIO2_END_OF_STREAM = 0x0040 # Used in XAUDIO2_BUFFER.Flags +XAUDIO2_SEND_USEFILTER = 0x0080 # Used in XAUDIO2_SEND_DESCRIPTOR.Flags +XAUDIO2_VOICE_NOSAMPLESPLAYED = 0x0100 # Used in IXAudio2SourceVoice::GetState +XAUDIO2_STOP_ENGINE_WHEN_IDLE = 0x2000 # Used in XAudio2Create to force the engine to Stop when no source voices are Started, and Start when a voice is Started +XAUDIO2_1024_QUANTUM = 0x8000 # Used in XAudio2Create to specify nondefault processing quantum of 21.33 ms (1024 samples at 48KHz) +XAUDIO2_NO_VIRTUAL_AUDIO_CLIENT = 0x10000 # Used in CreateMasteringVoice to create a virtual audio client + + +class IXAudio2VoiceCallback(com.Interface): + _methods_ = [ + ('OnVoiceProcessingPassStart', + com.STDMETHOD(UINT32)), + ('OnVoiceProcessingPassEnd', + com.STDMETHOD()), + ('onStreamEnd', + com.STDMETHOD()), + ('onBufferStart', + com.STDMETHOD(ctypes.c_void_p)), + ('OnBufferEnd', + com.STDMETHOD(ctypes.c_void_p)), + ('OnLoopEnd', + com.STDMETHOD(ctypes.c_void_p)), + ] + + +class XA2SourceCallback(com.COMObject): + """Callback class used to trigger when buffers or streams end.. + WARNING: Whenever a callback is running, XAudio2 cannot generate audio. + Make sure these functions run as fast as possible and do not block/delay more than a few milliseconds. + MS Recommendation: + At a minimum, callback functions must not do the following: + - Access the hard disk or other permanent storage + - Make expensive or blocking API calls + - Synchronize with other parts of client code + - Require significant CPU usage + """ + _interfaces_ = [IXAudio2VoiceCallback] + + def __init__(self, xa2_player): + self.xa2_player = xa2_player + + def OnVoiceProcessingPassStart(self, bytesRequired): + pass + + def OnVoiceProcessingPassEnd(self): + pass + + def onStreamEnd(self): + pass + + def onBufferStart(self, pBufferContext): + pass + + def OnBufferEnd(self, pBufferContext): + """At the end of playing one buffer, attempt to refill again. + Even if the player is out of sources, it needs to be called to purge all buffers. + """ + if self.xa2_player: + self.xa2_player.refill_source_player() + + def OnLoopEnd(self, this, pBufferContext): + pass + + def onVoiceError(self, this, pBufferContext, hresult): + raise Exception("Error occurred during audio playback.", hresult) + + +class XAUDIO2_EFFECT_DESCRIPTOR(Structure): + _fields_ = [ + ('pEffect', com.pIUnknown), + ('InitialState', c_bool), + ('OutputChannels', UINT32) + ] + + +class XAUDIO2_EFFECT_CHAIN(ctypes.Structure): + _fields_ = [ + ('EffectCount', UINT32), + ('pEffectDescriptors', POINTER(XAUDIO2_EFFECT_DESCRIPTOR)), + ] + + +class XAUDIO2_FILTER_PARAMETERS(Structure): + _fields_ = [ + ('Type', XAUDIO2_FILTER_TYPE), + ('Frequency', FLOAT), + ('OneOverQ', FLOAT) + ] + + +class XAUDIO2_VOICE_DETAILS(Structure): + _fields_ = [ + ('CreationFlags', UINT32), + ('ActiveFlags', UINT32), + ('InputChannels', UINT32), + ('InputSampleRate', UINT32) + ] + + +class IXAudio2Voice(com.pInterface): + _methods_ = [ + ('GetVoiceDetails', + com.STDMETHOD(POINTER(XAUDIO2_VOICE_DETAILS))), + ('SetOutputVoices', + com.STDMETHOD()), + ('SetEffectChain', + com.STDMETHOD(POINTER(XAUDIO2_EFFECT_CHAIN))), + ('EnableEffect', + com.STDMETHOD()), + ('DisableEffect', + com.STDMETHOD()), + ('GetEffectState', + com.STDMETHOD()), + ('SetEffectParameters', + com.STDMETHOD()), + ('GetEffectParameters', + com.STDMETHOD()), + ('SetFilterParameters', + com.STDMETHOD(POINTER(XAUDIO2_FILTER_PARAMETERS), UINT32)), + ('GetFilterParameters', + com.STDMETHOD()), + ('SetOutputFilterParameters', + com.STDMETHOD()), + ('GetOutputFilterParameters', + com.STDMETHOD()), + ('SetVolume', + com.STDMETHOD(ctypes.c_float, UINT32)), + ('GetVolume', + com.STDMETHOD(POINTER(c_float))), + ('SetChannelVolumes', + com.STDMETHOD()), + ('GetChannelVolumes', + com.STDMETHOD()), + ('SetOutputMatrix', + com.STDMETHOD(c_void_p, UINT32, UINT32, POINTER(FLOAT), UINT32)), + ('GetOutputMatrix', + com.STDMETHOD()), + ('DestroyVoice', + com.STDMETHOD()) + ] + + +class IXAudio2SubmixVoice(IXAudio2Voice): + pass + + +class IXAudio2SourceVoice(IXAudio2Voice): + _methods_ = [ + ('Start', + com.STDMETHOD(UINT32, UINT32)), + ('Stop', + com.STDMETHOD(UINT32, UINT32)), + ('SubmitSourceBuffer', + com.STDMETHOD(POINTER(XAUDIO2_BUFFER), c_void_p)), + ('FlushSourceBuffers', + com.STDMETHOD()), + ('Discontinuity', + com.STDMETHOD()), + ('ExitLoop', + com.STDMETHOD()), + ('GetState', + com.STDMETHOD(POINTER(XAUDIO2_VOICE_STATE), UINT32)), + ('SetFrequencyRatio', + com.STDMETHOD(FLOAT, UINT32)), + ('GetFrequencyRatio', + com.STDMETHOD(POINTER(c_float))), + ('SetSourceSampleRate', + com.STDMETHOD()), + ] + + +class IXAudio2MasteringVoice(IXAudio2Voice): + _methods_ = [ + ('GetChannelMask', + com.STDMETHOD(POINTER(DWORD))) + ] + + +class IXAudio2EngineCallback(com.Interface): + _methods_ = [ + ('OnProcessingPassStart', + com.METHOD(ctypes.c_void_p)), + ('OnProcessingPassEnd', + com.METHOD(ctypes.c_void_p)), + ('OnCriticalError', + com.METHOD(ctypes.c_void_p, ctypes.c_void_p, ctypes.c_ulong)), + ] + + +class XA2EngineCallback(com.COMObject): + _interfaces_ = [IXAudio2EngineCallback] + + def OnProcessingPassStart(self): + pass + + def OnProcessingPassEnd(self): + pass + + def OnCriticalError(self, this, hresult): + raise Exception("Critical Error:", hresult) + + + +# -------------- 3D Audio Positioning---------- +class X3DAUDIO_DISTANCE_CURVE_POINT(ctypes.Structure): + _fields_ = [ + ('Distance', FLOAT32), + ('DSPSetting', FLOAT32) + ] + + +class X3DAUDIO_DISTANCE_CURVE(ctypes.Structure): + _fields_ = [ + ('pPoints', POINTER(X3DAUDIO_DISTANCE_CURVE_POINT)), + ('PointCount', UINT32) + ] + + +class X3DAUDIO_VECTOR(ctypes.Structure): + _fields_ = [ + ('x', c_float), + ('y', c_float), + ('z', c_float), + ] + + + +"""Cone: + Specifies directionality for a listener or single-channel emitter by + modifying DSP behaviour with respect to its front orientation. + This is modeled using two sound cones: an inner cone and an outer cone. + On/within the inner cone, DSP settings are scaled by the inner values. + On/beyond the outer cone, DSP settings are scaled by the outer values. + If on both the cones, DSP settings are scaled by the inner values only. + Between the two cones, the scaler is linearly interpolated between the + inner and outer values. Set both cone angles to 0 or X3DAUDIO_2PI for + omnidirectionality using only the outer or inner values respectively.""" +class X3DAUDIO_CONE(Structure): + _fields_ = [ + ('InnerAngle', FLOAT32), # inner cone angle in radians, must be within [0.0f, X3DAUDIO_2PI] + ('OuterAngle', FLOAT32), # outer cone angle in radians, must be within [InnerAngle, X3DAUDIO_2PI] + ('InnerVolume', FLOAT32), # volume level scaler on/within inner cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used + ('OuterVolume', FLOAT32), # volume level scaler on/beyond outer cone, used only for matrix calculations, must be within [0.0f, 2.0f] when used + ('InnerLPF', FLOAT32), # LPF (both direct and reverb paths) coefficient subtrahend on/within inner cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used + ('OuterLPF', FLOAT32), # LPF (both direct and reverb paths) coefficient subtrahend on/beyond outer cone, used only for LPF (both direct and reverb paths) calculations, must be within [0.0f, 1.0f] when used + ('InnerReverb', FLOAT32), # reverb send level scaler on/within inner cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used + ('OuterReverb', FLOAT32) # reverb send level scaler on/beyond outer cone, used only for reverb calculations, must be within [0.0f, 2.0f] when used + ] + + +class X3DAUDIO_LISTENER(Structure): + _fields_ = [ + ('OrientFront', X3DAUDIO_VECTOR), # orientation of front direction, used only for matrix and delay calculations or listeners with cones for matrix, LPF (both direct and reverb paths), and reverb calculations, must be normalized when used + ('OrientTop', X3DAUDIO_VECTOR), # orientation of top direction, used only for matrix and delay calculations, must be orthonormal with OrientFront when used + ('Position', X3DAUDIO_VECTOR), # position in user-defined world units, does not affect Velocity + ('Velocity', X3DAUDIO_VECTOR), # velocity vector in user-defined world units/second, used only for doppler calculations, does not affect Position + ('pCone', POINTER(X3DAUDIO_CONE)) # sound cone, used only for matrix, LPF (both direct and reverb paths), and reverb calculations, NULL specifies omnidirectionality + ] + + +class X3DAUDIO_EMITTER(Structure): + _fields_ = [ + ('pCone', POINTER(X3DAUDIO_CONE)), + ('OrientFront', X3DAUDIO_VECTOR), + ('OrientTop', X3DAUDIO_VECTOR), + ('Position', X3DAUDIO_VECTOR), + ('Velocity', X3DAUDIO_VECTOR), + ('InnerRadius', FLOAT32), + ('InnerRadiusAngle', FLOAT32), + ('ChannelCount', UINT32), + ('ChannelRadius', FLOAT32), + ('pChannelAzimuths', POINTER(FLOAT32)), + ('pVolumeCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)), + ('pLFECurve', POINTER(X3DAUDIO_DISTANCE_CURVE)), + ('pLPFDirectCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)), + ('pLPFReverbCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)), + ('pReverbCurve', POINTER(X3DAUDIO_DISTANCE_CURVE)), + ('CurveDistanceScaler', FLOAT32), + ('DopplerScaler', FLOAT32) + ] + + +class X3DAUDIO_DSP_SETTINGS(Structure): + _fields_ = [ + ('pMatrixCoefficients', POINTER(FLOAT)), # float array + ('pDelayTimes', POINTER(FLOAT32)), + ('SrcChannelCount', UINT32), + ('DstChannelCount', UINT32), + ('LPFDirectCoefficient', FLOAT32), + ('LPFReverbCoefficient', FLOAT32), + ('ReverbLevel', FLOAT32), + ('DopplerFactor', FLOAT32), + ('EmitterToListenerAngle', FLOAT32), + ('EmitterToListenerDistance', FLOAT32), + ('EmitterVelocityComponent', FLOAT32), + ('ListenerVelocityComponent', FLOAT32) + ] + +# Other constants that may or may not be used in X3D. + +SPEAKER_FRONT_LEFT = 0x00000001 +SPEAKER_FRONT_RIGHT = 0x00000002 +SPEAKER_FRONT_CENTER = 0x00000004 +SPEAKER_LOW_FREQUENCY = 0x00000008 +SPEAKER_BACK_LEFT = 0x00000010 +SPEAKER_BACK_RIGHT = 0x00000020 +SPEAKER_FRONT_LEFT_OF_CENTER = 0x00000040 +SPEAKER_FRONT_RIGHT_OF_CENTER = 0x00000080 +SPEAKER_BACK_CENTER = 0x00000100 +SPEAKER_SIDE_LEFT = 0x00000200 +SPEAKER_SIDE_RIGHT = 0x00000400 +SPEAKER_TOP_CENTER = 0x00000800 +SPEAKER_TOP_FRONT_LEFT = 0x00001000 +SPEAKER_TOP_FRONT_CENTER = 0x00002000 +SPEAKER_TOP_FRONT_RIGHT = 0x00004000 +SPEAKER_TOP_BACK_LEFT = 0x00008000 +SPEAKER_TOP_BACK_CENTER = 0x00010000 +SPEAKER_TOP_BACK_RIGHT = 0x00020000 +SPEAKER_RESERVED = 0x7FFC0000 # bit mask locations reserved for future use +SPEAKER_ALL = 0x80000000 # used to specify that any possible permutation of speaker configurations + +SPEAKER_MONO = SPEAKER_FRONT_CENTER +SPEAKER_STEREO = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT) +SPEAKER_2POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY) +SPEAKER_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_BACK_CENTER) +SPEAKER_QUAD = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +SPEAKER_4POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +SPEAKER_5POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT) +SPEAKER_7POINT1 = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_FRONT_LEFT_OF_CENTER | SPEAKER_FRONT_RIGHT_OF_CENTER) +SPEAKER_5POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) +SPEAKER_7POINT1_SURROUND = (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY | SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT) + + +DBL_DECIMAL_DIG = 17 # # of decimal digits of rounding precision +DBL_DIG = 15 # # of decimal digits of precision +DBL_EPSILON = 2.2204460492503131e-016 # smallest such that 1.0+DBL_EPSILON != 1.0 +DBL_HAS_SUBNORM = 1 # type does support subnormal numbers +DBL_MANT_DIG = 53 # # of bits in mantissa +DBL_MAX = 1.7976931348623158e+308 # max value +DBL_MAX_10_EXP = 308 # max decimal exponent +DBL_MAX_EXP = 1024 # max binary exponent +DBL_MIN = 2.2250738585072014e-308 # min positive value +DBL_MIN_10_EXP = (-307) # min decimal exponent +DBL_MIN_EXP = (-1021) # min binary exponent +_DBL_RADIX = 2 # exponent radix +DBL_TRUE_MIN = 4.9406564584124654e-324 # min positive value + +FLT_DECIMAL_DIG = 9 # # of decimal digits of rounding precision +FLT_DIG = 6 # # of decimal digits of precision +FLT_EPSILON = 1.192092896e-07 # smallest such that 1.0+FLT_EPSILON != 1.0 +FLT_HAS_SUBNORM = 1 # type does support subnormal numbers +FLT_GUARD = 0 +FLT_MANT_DIG = 24 # # of bits in mantissa +FLT_MAX = 3.402823466e+38 # max value +FLT_MAX_10_EXP = 38 # max decimal exponent +FLT_MAX_EXP = 128 # max binary exponent +FLT_MIN = 1.175494351e-38 # min normalized positive value +FLT_MIN_10_EXP = (-37) # min decimal exponent +FLT_MIN_EXP = (-125) # min binary exponent +FLT_NORMALIZE = 0 +FLT_RADIX = 2 # exponent radix +FLT_TRUE_MIN = 1.401298464e-45 # min positive value + +LDBL_DIG = DBL_DIG # # of decimal digits of precision +LDBL_EPSILON = DBL_EPSILON # smallest such that 1.0+LDBL_EPSILON != 1.0 +LDBL_HAS_SUBNORM = DBL_HAS_SUBNORM # type does support subnormal numbers +LDBL_MANT_DIG = DBL_MANT_DIG # # of bits in mantissa +LDBL_MAX = DBL_MAX # max value +LDBL_MAX_10_EXP = DBL_MAX_10_EXP # max decimal exponent +LDBL_MAX_EXP = DBL_MAX_EXP # max binary exponent +LDBL_MIN = DBL_MIN # min normalized positive value +LDBL_MIN_10_EXP = DBL_MIN_10_EXP # min decimal exponent +LDBL_MIN_EXP = DBL_MIN_EXP # min binary exponent +_LDBL_RADIX = _DBL_RADIX # exponent radix +LDBL_TRUE_MIN = DBL_TRUE_MIN # min positive value + +DECIMAL_DIG = DBL_DECIMAL_DIG + + +X3DAUDIO_HANDLE_BYTESIZE = 20 +X3DAUDIO_HANDLE = (BYTE * X3DAUDIO_HANDLE_BYTESIZE) + + +# speed of sound in meters per second for dry air at approximately 20C, used with X3DAudioInitialize +X3DAUDIO_SPEED_OF_SOUND = 343.5 + + +X3DAUDIO_CALCULATE_MATRIX = 0x00000001 # enable matrix coefficient table calculation +X3DAUDIO_CALCULATE_DELAY = 0x00000002 # enable delay time array calculation (stereo final mix only) +X3DAUDIO_CALCULATE_LPF_DIRECT = 0x00000004 # enable LPF direct-path coefficient calculation +X3DAUDIO_CALCULATE_LPF_REVERB = 0x00000008 # enable LPF reverb-path coefficient calculation +X3DAUDIO_CALCULATE_REVERB = 0x00000010 # enable reverb send level calculation +X3DAUDIO_CALCULATE_DOPPLER = 0x00000020 # enable doppler shift factor calculation +X3DAUDIO_CALCULATE_EMITTER_ANGLE = 0x00000040 # enable emitter-to-listener interior angle calculation +X3DAUDIO_CALCULATE_ZEROCENTER = 0x00010000 # do not position to front center speaker, signal positioned to remaining speakers instead, front center destination channel will be zero in returned matrix coefficient table, valid only for matrix calculations with final mix formats that have a front center channel +X3DAUDIO_CALCULATE_REDIRECT_TO_LFE = 0x00020000 # apply equal mix of all source channels to LFE destination channel, valid only for matrix calculations with sources that have no LFE channel and final mix formats that have an LFE channel + +default_dsp_calculation = X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_DOPPLER + +X3DAudioInitialize = x3d_lib.X3DAudioInitialize +X3DAudioInitialize.restype = HRESULT +X3DAudioInitialize.argtypes = [c_int, c_float, c_void_p] + + +X3DAudioCalculate = x3d_lib.X3DAudioCalculate +X3DAudioCalculate.restype = c_void +X3DAudioCalculate.argtypes = [POINTER(X3DAUDIO_HANDLE), POINTER(X3DAUDIO_LISTENER), POINTER(X3DAUDIO_EMITTER), UINT32, POINTER(X3DAUDIO_DSP_SETTINGS)] + + +AudioCategory_Other = 0 +AudioCategory_ForegroundOnlyMedia = 1 +AudioCategory_Communications = 3 +AudioCategory_Alerts = 4 +AudioCategory_SoundEffects = 5 +AudioCategory_GameEffects = 6 +AudioCategory_GameMedia = 7 +AudioCategory_GameChat = 8 +AudioCategory_Speech = 9 +AudioCategory_Movie = 10 +AudioCategory_Media = 11 + +# Reverb not implemented but if someone wants to take a stab at it. +class XAUDIO2FX_REVERB_PARAMETERS(Structure): + _fields_ = [ + ('WetDryMix', c_float), # ratio of wet (processed) signal to dry (original) signal + + # Delay times + ('ReflectionsDelay', UINT32), # [0, 300] in ms + ('ReverbDelay', BYTE), # [0, 85] in ms + ('RearDelay', UINT32), # 7.1: [0, 20] in ms, all other: [0, 5] in ms + ('SideDelay', UINT32), # .1: [0, 5] in ms, all other: not used, but still validated # WIN 10 only. + + # Indexed Paremeters + ('PositionLeft', BYTE), # [0, 30] no units + ('PositionRight', BYTE), # 0, 30] no units, ignored when configured to mono + ('PositionMatrixLeft', BYTE), # [0, 30] no units + ('PositionMatrixRight', BYTE), # [0, 30] no units, ignored when configured to mono + ('EarlyDiffusion', BYTE), # [0, 15] no units + ('LateDiffusion', BYTE), # [0, 15] no units + ('LowEQGain', BYTE), # [0, 12] no units + ('LowEQCutoff', BYTE), # [0, 9] no units + ('LowEQCutoff', BYTE), # [0, 8] no units + ('HighEQCutoff', BYTE), # [0, 14] no units + + # Direct parameters + ('RoomFilterFreq', c_float), # [20, 20000] in Hz + ('RoomFilterMain', c_float), # [-100, 0] in dB + ('RoomFilterHF', c_float), # [-100, 0] in dB + ('ReflectionsGain', c_float), # [-100, 20] in dB + ('ReverbGain', c_float), # [-100, 20] in dB + ('DecayTime', c_float), # [0.1, inf] in seconds + ('Density', c_float), # [0, 100] (percentage) + ('RoomSize', c_float), # [1, 100] in feet + + # component control + ('DisableLateField', c_bool), # TRUE to disable late field reflections + ] + + +class IXAudio2(com.pIUnknown): + _methods_ = [ + ('RegisterForCallbacks', + com.STDMETHOD(POINTER(IXAudio2EngineCallback))), + ('UnregisterForCallbacks', + com.METHOD(ctypes.c_void_p, POINTER(IXAudio2EngineCallback))), + ('CreateSourceVoice', + com.STDMETHOD(POINTER(IXAudio2SourceVoice), POINTER(WAVEFORMATEX), UINT32, c_float, + POINTER(IXAudio2VoiceCallback), POINTER(XAUDIO2_VOICE_SENDS), POINTER(XAUDIO2_EFFECT_CHAIN))), + ('CreateSubmixVoice', + com.STDMETHOD(POINTER(IXAudio2SubmixVoice), UINT32, UINT32, UINT32, UINT32, + POINTER(XAUDIO2_VOICE_SENDS), POINTER(XAUDIO2_EFFECT_CHAIN))), + ('CreateMasteringVoice', + com.STDMETHOD(POINTER(IXAudio2MasteringVoice), UINT32, UINT32, UINT32, LPCWSTR, POINTER(XAUDIO2_EFFECT_CHAIN), + UINT32)), + ('StartEngine', + com.STDMETHOD()), + ('StopEngine', + com.STDMETHOD()), + ('CommitChanges', + com.STDMETHOD(UINT32)), + ('GetPerformanceData', + com.METHOD(c_void, POINTER(XAUDIO2_PERFORMANCE_DATA))), + ('SetDebugConfiguration', + com.STDMETHOD(POINTER(XAUDIO2_DEBUG_CONFIGURATION), c_void_p)), + ] + + +XAudio2Create = xaudio2_lib.XAudio2Create +XAudio2Create.restype = HRESULT +XAudio2Create.argtypes = [POINTER(IXAudio2), UINT32, UINT32] + +CreateAudioReverb = xaudio2_lib.CreateAudioReverb +CreateAudioReverb.restype = HRESULT +CreateAudioReverb.argtypes = [POINTER(com.pIUnknown)] + diff -Nru pyglet-1.4.10/pyglet/media/events.py pyglet-1.5.14/pyglet/media/events.py --- pyglet-1.4.10/pyglet/media/events.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/events.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -from builtins import object # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -39,7 +38,7 @@ import pyglet -class MediaEvent(object): +class MediaEvent: """Representation of a media event. These events are used internally by some audio driver implementation to diff -Nru pyglet-1.4.10/pyglet/media/__init__.py pyglet-1.5.14/pyglet/media/__init__.py --- pyglet-1.4.10/pyglet/media/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/__init__.py 2020-11-10 10:36:26.000000000 +0000 @@ -85,9 +85,6 @@ from . import synthesis -# deprecated:: 1.4 -# Procedural was renamed to `synthesis` in 1.4 -from . import synthesis as procedural __all__ = ( 'load', diff -Nru pyglet-1.4.10/pyglet/media/instrumentation.py pyglet-1.5.14/pyglet/media/instrumentation.py --- pyglet-1.4.10/pyglet/media/instrumentation.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/instrumentation.py 2020-11-10 10:36:26.000000000 +0000 @@ -45,12 +45,13 @@ # events definition mp_events = { "version": 1.1, -# : { -# "desc": , -# "update_names": , -# "other_fields": -# }, + + # : { + # "desc": , + # "update_names": , + # "other_fields": + # }, + "crash": { "desc": "media_player crashed.", "update_names": ["evname", "sample"], @@ -133,7 +134,7 @@ mp_bads = {"crash", "p.P.ut.1.5", "p.P.ut.1.7", "p.P.ut.1.8"} -class MediaPlayerStateIterator(object): +class MediaPlayerStateIterator: """Exposes for analysis the sequence of media_player states Typical use @@ -199,7 +200,7 @@ self.state["frame_num"] += 1 -class TimelineBuilder(object): +class TimelineBuilder: """At each call to player.Player.update_texture we capture selected player state, before accepting the changes in the event. This is the same as capturing the state at the end of previous update call. @@ -260,7 +261,7 @@ return crashed -class CountBads(object): +class CountBads: """Helper to report anomalies in the media_player states seen when playing a sample. @@ -281,7 +282,6 @@ anomalies_description["scheduling_in_past"] = "Scheduling in the past" return anomalies_description - def preprocessing(self, recorded_events): """ I see all recordings ending with some potential anomalies in the few @@ -291,24 +291,20 @@ """ recorded_events = list(recorded_events) if (len(recorded_events) > 9 and - recorded_events[-2][0] == "p.P.ut.1.7" and - recorded_events[-6][0] == "p.P.ut.1.7" and - recorded_events[-10][0] == "p.P.ut.1.7" - ): + recorded_events[-2][0] == "p.P.ut.1.7" and + recorded_events[-6][0] == "p.P.ut.1.7" and + recorded_events[-10][0] == "p.P.ut.1.7"): del recorded_events[-10] del recorded_events[-6] del recorded_events[-2] elif (len(recorded_events) > 6 and recorded_events[-2][0] == "p.P.ut.1.7" and - recorded_events[-6][0] == "p.P.ut.1.7" - ): + recorded_events[-6][0] == "p.P.ut.1.7"): del recorded_events[-6] del recorded_events[-2] - elif (len(recorded_events) > 2 and - recorded_events[-2][0] == "p.P.ut.1.7" - ): + elif len(recorded_events) > 2 and recorded_events[-2][0] == "p.P.ut.1.7": del recorded_events[-2] return recorded_events diff -Nru pyglet-1.4.10/pyglet/media/player.py pyglet-1.5.14/pyglet/media/player.py --- pyglet-1.4.10/pyglet/media/player.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/player.py 2020-12-31 21:03:32.000000000 +0000 @@ -1,7 +1,3 @@ -"""High-level sound and video player.""" -from __future__ import print_function -from __future__ import division -from builtins import object # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -36,69 +32,66 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- +"""High-level sound and video player.""" +import time from collections import deque import pyglet from pyglet.media import buffered_logger as bl from pyglet.media.drivers import get_audio_driver -from pyglet.media.codecs.base import Source - -# import cProfile +from pyglet.media.codecs.base import Source, SourceGroup _debug = pyglet.options['debug_media'] -clock = pyglet.clock.get_default() +class PlaybackTimer: + """Playback Timer. -class MasterClock(object): - """Master clock object. - - This is a simple clock object which tracks the time elapsed. It can be + This is a simple timer object which tracks the time elapsed. It can be paused and reset. """ def __init__(self): - """Initialize the clock with time 0.""" + """Initialize the timer with time 0.""" self._time = 0.0 self._systime = None - def play(self): - """Start the clock.""" - self._systime = clock.time() + def start(self): + """Start the timer.""" + self._systime = time.time() def pause(self): - """Pause the clock.""" + """Pause the timer.""" self._time = self.get_time() self._systime = None def reset(self): - """Reset the clock to 0.""" + """Reset the timer to 0.""" self._time = 0.0 if self._systime is not None: - self._systime = clock.time() + self._systime = time.time() def get_time(self): - """Current master clock time.""" + """Get the elapsed time.""" if self._systime is None: now = self._time else: - now = clock.time() - self._systime + self._time + now = time.time() - self._systime + self._time return now def set_time(self, value): """ - Set the master clock time. + Manually set the elapsed time. Args: - value (float): The new :class:`~pyglet.media.player.MasterClock` - time. + value (float): the new elapsed time value """ self.reset() self._time = value -class _PlayerProperty(object): +class _PlayerProperty: """Descriptor for Player attributes to forward to the AudioPlayer. We want the Player to have attributes like volume, pitch, etc. These are @@ -153,7 +146,7 @@ # Desired play state (not an indication of actual state). self._playing = False - self._mclock = MasterClock() + self._timer = PlaybackTimer() #: Loop the current source indefinitely or until #: :meth:`~Player.next_source` is called. Defaults to ``False``. #: @@ -162,8 +155,6 @@ #: .. versionadded:: 1.4 self.loop = False - # self.pr = cProfile.Profile() - def __del__(self): """Release the Player resources.""" self.delete() @@ -178,7 +169,7 @@ Args: source (Source or Iterable[Source]): The source to queue. """ - if isinstance(source, Source): + if isinstance(source, (Source, SourceGroup)): source = _one_item_playlist(source) else: try: @@ -225,16 +216,17 @@ # add a delay to de-synchronize the audio. # Negative number means audio runs ahead. # self._mclock._systime += -0.3 - self._mclock.play() + self._timer.start() if self._audio_player is None and source.video_format is None: pyglet.clock.schedule_once(lambda dt: self.dispatch_event("on_eos"), source.duration) + else: if self._audio_player: self._audio_player.stop() pyglet.clock.unschedule(self.update_texture) - self._mclock.pause() + self._timer.pause() @property def playing(self): @@ -283,9 +275,9 @@ """ was_playing = self._playing self.pause() - self._mclock.reset() + self._timer.reset() - if self.source: + if self._source: # Reset source to the beginning self.seek(0.0) self.source.is_player_source = False @@ -295,38 +287,39 @@ return try: - source = next(playlists[0]) + new_source = next(playlists[0]) except StopIteration: self._playlists.popleft() if not self._playlists: - source = None + new_source = None else: # Could someone queue an iterator which is empty?? - source = next(self._playlists[0]) + new_source = next(self._playlists[0]) - if source is None: + if new_source is None: self._source = None self.delete() self.dispatch_event('on_player_eos') else: - old_audio_format = self.source.audio_format - old_video_format = self.source.video_format - self._source = source.get_queue_source() + old_audio_format = self._source.audio_format + old_video_format = self._source.video_format + self._source = new_source.get_queue_source() - if old_audio_format == self.source.audio_format: - self._audio_player.clear() - self._audio_player.source = self.source - else: - self._audio_player.delete() - self._audio_player = None - if old_video_format != self.source.video_format: + if self._audio_player: + if old_audio_format == self._source.audio_format: + self._audio_player.clear() + self._audio_player.source = self._source + else: + self._audio_player.delete() + self._audio_player = None + if old_video_format != self._source.video_format: self._texture = None pyglet.clock.unschedule(self.update_texture) self._set_playing(was_playing) self.dispatch_event('on_player_next_source') - def seek(self, time): + def seek(self, timestamp): """ Seek for playback to the indicated timestamp on the current source. @@ -334,7 +327,7 @@ duration of the source, it will be clamped to the end. Args: - time (float): The time where to seek in the source, clamped to the + timestamp (float): The time where to seek in the source, clamped to the beginning and end of the source. """ playing = self._playing @@ -344,10 +337,12 @@ return if bl.logger is not None: - bl.logger.log("p.P.sk", time) + bl.logger.log("p.P.sk", timestamp) + + timestamp = max(timestamp, 0) - self._mclock.set_time(time) - self.source.seek(time) + self._timer.set_time(timestamp) + self._source.seek(timestamp) if self._audio_player: # XXX: According to docstring in AbstractAudioPlayer this cannot # be called when the player is not stopped @@ -391,7 +386,7 @@ player master clock time which is used to synchronize both the audio and the video. """ - return self._mclock.get_time() + return self._timer.get_time() def _create_texture(self): video_format = self.source.video_format @@ -607,12 +602,13 @@ if self.loop: was_playing = self._playing self.pause() - self._mclock.reset() + self._timer.reset() if self.source: # Reset source to the beginning self.seek(0.0) - self._audio_player.clear() + if self._audio_player: + self._audio_player.clear() self._set_playing(was_playing) else: @@ -628,17 +624,38 @@ """ pass + def on_driver_reset(self): + """The audio driver has been reset, by default this will kill the current audio player and create a new one, + and requeue the buffers. Any buffers that may have been queued in a player will be resubmitted. It will + continue from from the last buffers submitted, not played and may cause sync issues if using video. + + :event: + """ + if self._audio_player: + self._audio_player.on_driver_reset() + + # Voice has been changed, will need to reset all options on the voice. + for attr in ('volume', 'min_distance', 'max_distance', 'position', + 'pitch', 'cone_orientation', 'cone_inner_angle', + 'cone_outer_angle', 'cone_outer_gain'): + value = getattr(self, attr) + setattr(self, attr, value) + + if self._playing: + self._audio_player.play() + Player.register_event_type('on_eos') Player.register_event_type('on_player_eos') Player.register_event_type('on_player_next_source') +Player.register_event_type('on_driver_reset') def _one_item_playlist(source): yield source -class PlayerGroup(object): +class PlayerGroup: """Group of players that can be played and paused simultaneously. Create a player group for the given list of players. diff -Nru pyglet-1.4.10/pyglet/media/synthesis.py pyglet-1.5.14/pyglet/media/synthesis.py --- pyglet-1.4.10/pyglet/media/synthesis.py 2020-01-17 18:18:56.000000000 +0000 +++ pyglet-1.5.14/pyglet/media/synthesis.py 2020-12-31 21:03:32.000000000 +0000 @@ -1,5 +1,3 @@ -from __future__ import division -from builtins import range # ---------------------------------------------------------------------------- # pyglet # Copyright (c) 2006-2008 Alex Holkner @@ -35,18 +33,18 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from .codecs.base import Source, AudioFormat, AudioData - -from collections import deque - -import ctypes import os import math import struct import random +import ctypes + +from .codecs.base import Source, AudioFormat, AudioData + +from collections import deque -class Envelope(object): +class Envelope: """Base class for SynthesisSource amplitude envelopes.""" def get_generator(self, sample_rate, duration): raise NotImplementedError @@ -312,7 +310,7 @@ envelope = self._envelope_generator for i in range(samples): data[i] = int(math.sin(step * i) * amplitude * next(envelope) + bias) - return data + return bytes(data) class Triangle(SynthesisSource): @@ -356,7 +354,7 @@ value = minimum - (value - minimum) step = -step data[i] = int(value * next(envelope)) - return data + return bytes(data) class Sawtooth(SynthesisSource): @@ -396,7 +394,7 @@ if value > maximum: value = minimum + (value % maximum) data[i] = int(value * next(envelope)) - return data + return bytes(data) class Square(SynthesisSource): @@ -438,7 +436,7 @@ count %= half_period count += 1 data[i] = int(value * amplitude * next(envelope) + bias) - return data + return bytes(data) class FM(SynthesisSource): @@ -491,7 +489,7 @@ increment = i / sample_rate data[i] = int(sin(car_step * increment + mod_index * sin(mod_step * increment)) * amplitude * next(envelope) + bias) - return data + return bytes(data) class Digitar(SynthesisSource): @@ -539,4 +537,4 @@ for i in range(samples): data[i] = int(ring_buffer[0] * amplitude + bias) ring_buffer.append(decay * (ring_buffer[0] + ring_buffer[1]) / 2) - return data + return bytes(data) diff -Nru pyglet-1.4.10/pyglet/model/codecs/gltf.py pyglet-1.5.14/pyglet/model/codecs/gltf.py --- pyglet-1.4.10/pyglet/model/codecs/gltf.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/model/codecs/gltf.py 2020-11-10 10:36:26.000000000 +0000 @@ -102,7 +102,7 @@ } -class Buffer(object): +class Buffer: # TODO: support GLB format # TODO: support data uris def __init__(self, length, uri): @@ -117,7 +117,7 @@ return data -class BufferView(object): +class BufferView: def __init__(self, buffer, offset, length, target, stride): self.buffer = buffer self.offset = offset @@ -126,7 +126,7 @@ self.stride = stride -class Accessor(object): +class Accessor: # TODO: support sparse accessors def __init__(self, buffer_view, offset, comp_type, count, maximum, minimum, accessor_type, sparse): diff -Nru pyglet-1.4.10/pyglet/model/codecs/__init__.py pyglet-1.5.14/pyglet/model/codecs/__init__.py --- pyglet-1.4.10/pyglet/model/codecs/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/model/codecs/__init__.py 2020-11-16 14:49:52.000000000 +0000 @@ -32,30 +32,27 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -import os.path +from pyglet.util import Codecs, Decoder, Encoder, DecodeException, EncodeException -_decoders = [] # List of registered ModelDecoders -_encoders = [] # List of registered ModelEncoders -_decoder_extensions = {} # Map str -> list of matching ModelDecoders -_encoder_extensions = {} # Map str -> list of matching ModelEncoders +_codecs = Codecs() -class ModelDecodeException(Exception): - exception_priority = 10 +add_decoders = _codecs.add_decoders +get_decoders = _codecs.get_decoders +add_encoders = _codecs.add_encoders +get_encoders = _codecs.get_encoders -class ModelEncodeException(Exception): +class ModelDecodeException(DecodeException): pass -class ModelDecoder(object): - def get_file_extensions(self): - """Return a list of accepted file extensions, e.g. ['.obj', '.gox'] - Lower-case only. - """ - return [] +class ModelEncodeException(EncodeException): + pass + +class ModelDecoder(Decoder): def decode(self, file, filename, batch): """Decode the given file object and return an instance of `Model`. Throws ModelDecodeException if there is an error. filename @@ -63,16 +60,8 @@ """ raise NotImplementedError() - def __repr__(self): - return "{0}{1}".format(self.__class__.__name__, self.get_file_extensions()) - -class ModelEncoder(object): - def get_file_extensions(self): - """Return a list of accepted file extensions, e.g. ['.obj', '.gox'] - Lower-case only. - """ - return [] +class ModelEncoder(Encoder): def encode(self, model, file, filename): """Encode the given model to the given file. filename @@ -82,58 +71,6 @@ """ raise NotImplementedError() - def __repr__(self): - return "{0}{1}".format(self.__class__.__name__, self.get_file_extensions()) - - -def get_encoders(filename=None): - """Get an ordered list of all encoders. If a `filename` is provided, - encoders supporting that extension will be ordered first in the list. - """ - encoders = [] - if filename: - extension = os.path.splitext(filename)[1].lower() - encoders += _encoder_extensions.get(extension, []) - encoders += [e for e in _encoders if e not in encoders] - return encoders - - -def get_decoders(filename=None): - """Get an ordered list of all decoders. If a `filename` is provided, - decoders supporting that extension will be ordered first in the list. - """ - decoders = [] - if filename: - extension = os.path.splitext(filename)[1].lower() - decoders += _decoder_extensions.get(extension, []) - return decoders - - -def add_decoders(module): - """Add a decoder module. The module must define `get_decoders`. Once - added, the appropriate decoders defined in the codec will be returned by - pyglet.model.codecs.get_decoders. - """ - for decoder in module.get_decoders(): - _decoders.append(decoder) - for extension in decoder.get_file_extensions(): - if extension not in _decoder_extensions: - _decoder_extensions[extension] = [] - _decoder_extensions[extension].append(decoder) - - -def add_encoders(module): - """Add an encoder module. The module must define `get_encoders`. Once - added, the appropriate encoders defined in the codec will be returned by - pyglet.model.codecs.get_encoders. - """ - for encoder in module.get_encoders(): - _encoders.append(encoder) - for extension in encoder.get_file_extensions(): - if extension not in _encoder_extensions: - _encoder_extensions[extension] = [] - _encoder_extensions[extension].append(encoder) - def add_default_model_codecs(): # Add all bundled codecs. These should be listed in order of diff -Nru pyglet-1.4.10/pyglet/model/codecs/obj.py pyglet-1.5.14/pyglet/model/codecs/obj.py --- pyglet-1.4.10/pyglet/model/codecs/obj.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/model/codecs/obj.py 2020-12-23 15:47:40.000000000 +0000 @@ -42,7 +42,7 @@ from . import ModelDecodeException, ModelDecoder -class Mesh(object): +class Mesh: def __init__(self, name): self.name = name self.material = None @@ -117,10 +117,12 @@ file_contents = f.read() else: file_contents = file.read() - file.close() if hasattr(file_contents, 'decode'): - file_contents = file_contents.decode() + try: + file_contents = file_contents.decode() + except UnicodeDecodeError as e: + raise ModelDecodeException("Unable to decode obj: {}".format(e)) material = None mesh = None diff -Nru pyglet-1.4.10/pyglet/model/__init__.py pyglet-1.5.14/pyglet/model/__init__.py --- pyglet-1.4.10/pyglet/model/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/model/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -82,11 +82,8 @@ .. versionadded:: 1.4 """ -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +import io - -from pyglet.compat import BytesIO from pyglet.gl import * from pyglet import graphics @@ -125,7 +122,7 @@ file = open(filename, 'rb') if not hasattr(file, 'seek'): - file = BytesIO(file.read()) + file = io.BytesIO(file.read()) try: if decoder: @@ -149,7 +146,7 @@ file.close() -class Model(object): +class Model: """Instance of a 3D object. See the module documentation for usage. @@ -227,7 +224,7 @@ self._batch.draw_subset(self.vertex_lists) -class Material(object): +class Material: __slots__ = ("name", "diffuse", "ambient", "specular", "emission", "shininess", "texture_name") def __init__(self, name, diffuse, ambient, specular, emission, shininess, texture_name=None): diff -Nru pyglet-1.4.10/pyglet/resource.py pyglet-1.5.14/pyglet/resource.py --- pyglet-1.4.10/pyglet/resource.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/resource.py 2020-12-23 15:47:40.000000000 +0000 @@ -84,21 +84,14 @@ .. versionadded:: 1.1 """ -from future import standard_library - -standard_library.install_aliases() -from builtins import object, str - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' +import io import os -import weakref import sys import zipfile +import weakref import pyglet -from pyglet.compat import BytesIO class ResourceNotFoundException(Exception): @@ -192,7 +185,7 @@ return os.path.expanduser('~/.%s' % name) -class Location(object): +class Location: """Abstract resource location. Given a location, a file can be loaded from that location with the `open` @@ -261,7 +254,7 @@ forward_slash_path = path.replace(os.sep, '/') # zip can only handle forward slashes text = self.zip.read(forward_slash_path) - return BytesIO(text) + return io.BytesIO(text) class URLLocation(Location): @@ -287,7 +280,7 @@ return urllib.request.urlopen(url) -class Loader(object): +class Loader: """Load program resource files from disk. The loader contains a search path which can include filesystem @@ -433,7 +426,7 @@ volume_index += 1 - zip_stream = BytesIO(bytes_) + zip_stream = io.BytesIO(bytes_) if zipfile.is_zipfile(zip_stream): return zip_stream else: @@ -503,7 +496,7 @@ file = self.file(name) font.add_file(file) - def _alloc_image(self, name, atlas=True): + def _alloc_image(self, name, atlas, border): file = self.file(name) try: img = pyglet.image.load(name, file=file) @@ -514,20 +507,20 @@ return img.get_texture(True) # find an atlas suitable for the image - bin = self._get_texture_atlas_bin(img.width, img.height) + bin = self._get_texture_atlas_bin(img.width, img.height, border) if bin is None: return img.get_texture(True) - return bin.add(img) + return bin.add(img, border) - def _get_texture_atlas_bin(self, width, height): + def _get_texture_atlas_bin(self, width, height, border): """A heuristic for determining the atlas bin to use for a given image size. Returns None if the image should not be placed in an atlas (too big), otherwise the bin (a list of TextureAtlas). """ # Large images are not placed in an atlas max_texture_size = pyglet.image.get_max_texture_size() - max_size = min(2048, max_texture_size) + max_size = min(2048, max_texture_size) - border if width > max_size or height > max_size: return None @@ -540,12 +533,12 @@ try: texture_bin = self._texture_atlas_bins[bin_size] except KeyError: - texture_bin = pyglet.image.atlas.TextureBin(border=True) + texture_bin = pyglet.image.atlas.TextureBin() self._texture_atlas_bins[bin_size] = texture_bin return texture_bin - def image(self, name, flip_x=False, flip_y=False, rotate=0, atlas=True): + def image(self, name, flip_x=False, flip_y=False, rotate=0, atlas=True, border=1): """Load an image with optional transformation. This is similar to `texture`, except the resulting image will be @@ -567,6 +560,9 @@ pyglet. If atlas loading is not appropriate for specific texturing reasons (e.g. border control is required) then set this argument to False. + `border` : int + Leaves specified pixels of blank space around each image in + an atlas, which may help reduce texture bleeding. :rtype: `Texture` :return: A complete texture if the image is large or not in an atlas, @@ -576,14 +572,14 @@ if name in self._cached_images: identity = self._cached_images[name] else: - identity = self._cached_images[name] = self._alloc_image(name, atlas=atlas) + identity = self._cached_images[name] = self._alloc_image(name, atlas, border) if not rotate and not flip_x and not flip_y: return identity return identity.get_transform(flip_x, flip_y, rotate) - def animation(self, name, flip_x=False, flip_y=False, rotate=0): + def animation(self, name, flip_x=False, flip_y=False, rotate=0, border=1): """Load an animation with optional transformation. Animations loaded from the same source but with different @@ -599,7 +595,10 @@ `rotate` : int The returned image will be rotated clockwise by the given number of degrees (a multiple of 90). - + `border` : int + Leaves specified pixels of blank space around each image in + an atlas, which may help reduce texture bleeding. + :rtype: :py:class:`~pyglet.image.Animation` """ self._require_index() @@ -608,9 +607,10 @@ except KeyError: animation = pyglet.image.load_animation(name, self.file(name)) bin = self._get_texture_atlas_bin(animation.get_max_width(), - animation.get_max_height()) + animation.get_max_height(), + border) if bin: - animation.add_to_texture_bin(bin) + animation.add_to_texture_bin(bin, border) identity = self._cached_animations[name] = animation diff -Nru pyglet-1.4.10/pyglet/shapes.py pyglet-1.5.14/pyglet/shapes.py --- pyglet-1.4.10/pyglet/shapes.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/shapes.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,890 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""2D shapes. + +This module provides classes for a variety of simplistic 2D shapes, +such as Rectangles, Circles, and Lines. These shapes are are made +internally from OpenGL primitives, and provide excellent performance +when drawn as part of a :py:class:`~pyglet.graphics.Batch`. +Convenience methods are provided for positioning, changing color +and opacity, and rotation (where applicible). To create more +complex shapes than what is provided here, the lower evel +graphics API is more appropriate. +See the :ref:`guide_graphics` for more details. + +A simple example of drawing shapes:: + + import pyglet + from pyglet import shapes + + window = pyglet.window.Window(960, 540) + batch = pyglet.graphics.Batch() + + circle = shapes.Circle(700, 150, 100, color=(50, 225, 30), batch=batch) + square = shapes.Rectangle(200, 200, 200, 200, color=(55, 55, 255), batch=batch) + rectangle = shapes.Rectangle(250, 300, 400, 200, color=(255, 22, 20), batch=batch) + rectangle.opacity = 128 + rectangle.rotation = 33 + line = shapes.Line(100, 100, 100, 200, width=19, batch=batch) + line2 = shapes.Line(150, 150, 444, 111, width=4, color=(200, 20, 20), batch=batch) + + @window.event + def on_draw(): + window.clear() + batch.draw() + + pyglet.app.run() + + + +.. versionadded:: 1.5.4 +""" + +import math + +from pyglet.gl import GL_COLOR_BUFFER_BIT, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA +from pyglet.gl import GL_TRIANGLES, GL_TRIANGLE_STRIP, GL_LINES, GL_BLEND +from pyglet.gl import glPushAttrib, glPopAttrib, glBlendFunc, glEnable, glDisable +from pyglet.graphics import Group, Batch + + +class _ShapeGroup(Group): + """Shared Shape rendering Group. + + The group is automatically coalesced with other shape groups + sharing the same parent group and blend parameters. + """ + + def __init__(self, blend_src, blend_dest, parent=None): + """Create a Shape group. + + The group is created internally. Usually you do not + need to explicitly create it. + + :Parameters: + `blend_src` : int + OpenGL blend source mode; for example, + ``GL_SRC_ALPHA``. + `blend_dest` : int + OpenGL blend destination mode; for example, + ``GL_ONE_MINUS_SRC_ALPHA``. + `parent` : `~pyglet.graphics.Group` + Optional parent group. + """ + super().__init__(parent) + self.blend_src = blend_src + self.blend_dest = blend_dest + + def set_state(self): + glPushAttrib(GL_COLOR_BUFFER_BIT) + glEnable(GL_BLEND) + glBlendFunc(self.blend_src, self.blend_dest) + + def unset_state(self): + glDisable(GL_BLEND) + glPopAttrib() + + def __eq__(self, other): + return (other.__class__ is self.__class__ and + self.parent is other.parent and + self.blend_src == other.blend_src and + self.blend_dest == other.blend_dest) + + def __hash__(self): + return hash((id(self.parent), self.blend_src, self.blend_dest)) + + +class _ShapeBase: + """Base class for Shape objects""" + + _rgb = (255, 255, 255) + _opacity = 255 + _visible = True + _x = 0 + _y = 0 + _anchor_x = 0 + _anchor_y = 0 + _batch = None + _group = None + _vertex_list = None + + def __del__(self): + try: + if self._vertex_list is not None: + self._vertex_list.delete() + except: + pass + + def _update_position(self): + raise NotImplementedError + + def _update_color(self): + raise NotImplementedError + + def draw(self): + """Draw the shape at its current position. + + Using this method is not recommended. Instead, add the + shape to a `pyglet.graphics.Batch` for efficient rendering. + """ + self._group.set_state_recursive() + self._vertex_list.draw(GL_TRIANGLES) + self._group.unset_state_recursive() + + def delete(self): + self._vertex_list.delete() + self._vertex_list = None + + @property + def x(self): + """X coordinate of the shape. + + :type: int or float + """ + return self._x + + @x.setter + def x(self, value): + self._x = value + self._update_position() + + @property + def y(self): + """Y coordinate of the shape. + + :type: int or float + """ + return self._y + + @y.setter + def y(self, value): + self._y = value + self._update_position() + + @property + def position(self): + """The (x, y) coordinates of the shape, as a tuple. + + :Parameters: + `x` : int or float + X coordinate of the sprite. + `y` : int or float + Y coordinate of the sprite. + """ + return self._x, self._y + + @position.setter + def position(self, values): + self._x, self._y = values + self._update_position() + + @property + def anchor_x(self): + """The X coordinate of the anchor point + + :type: int or float + """ + return self._anchor_x + + @anchor_x.setter + def anchor_x(self, value): + self._anchor_x = value + self._update_position() + + @property + def anchor_y(self): + """The Y coordinate of the anchor point + + :type: int or float + """ + return self._anchor_y + + @anchor_y.setter + def anchor_y(self, value): + self._anchor_y = value + self._update_position() + + @property + def anchor_position(self): + """The (x, y) coordinates of the anchor point, as a tuple. + + :Parameters: + `x` : int or float + X coordinate of the anchor point. + `y` : int or float + Y coordinate of the anchor point. + """ + return self._anchor_x, self._anchor_y + + @anchor_position.setter + def anchor_position(self, values): + self._anchor_x, self._anchor_y = values + self._update_position() + + @property + def color(self): + """The shape color. + + This property sets the color of the shape. + + The color is specified as an RGB tuple of integers '(red, green, blue)'. + Each color component must be in the range 0 (dark) to 255 (saturated). + + :type: (int, int, int) + """ + return self._rgb + + @color.setter + def color(self, values): + self._rgb = list(map(int, values)) + self._update_color() + + @property + def opacity(self): + """Blend opacity. + + This property sets the alpha component of the color of the shape. + With the default blend mode (see the constructor), this allows the + shape to be drawn with fractional opacity, blending with the + background. + + An opacity of 255 (the default) has no effect. An opacity of 128 + will make the shape appear translucent. + + :type: int + """ + return self._opacity + + @opacity.setter + def opacity(self, value): + self._opacity = value + self._update_color() + + @property + def visible(self): + """True if the shape will be drawn. + + :type: bool + """ + return self._visible + + @visible.setter + def visible(self, value): + self._visible = value + self._update_position() + + +class Arc(_ShapeBase): + def __init__(self, x, y, radius, segments=25, angle=math.pi * 2, color=(255, 255, 255), batch=None, group=None): + # TODO: Finish this shape and add docstring. + self._x = x + self._y = y + self._radius = radius + self._segments = segments + self._rgb = color + self._angle = angle + + self._batch = batch or Batch() + self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, group) + + self._vertex_list = self._batch.add(self._segments * 2, GL_LINES, self._group, 'v2f', 'c4B') + self._update_position() + self._update_color() + + def _update_position(self): + if not self._visible: + vertices = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + else: + x = self._x + self._anchor_x + y = self._y + self._anchor_y + r = self._radius + tau_segs = self._angle / (self._segments - 1) + + # Calcuate the outer points of the arc: + points = [(x + (r * math.cos(i * tau_segs)), + y + (r * math.sin(i * tau_segs))) for i in range(self._segments)] + + # Create a list of doubled-up points from the points: + vertices = [] + for i, point in enumerate(points): + line_points = *points[i - 1], *point + vertices.extend(line_points) + + self._vertex_list.vertices[:] = vertices + + def _update_color(self): + self._vertex_list.colors[:] = [*self._rgb, int(self._opacity)] * self._segments * 2 + + def draw(self): + """Draw the shape at its current position. + + Using this method is not recommended. Instead, add the + shape to a `pyglet.graphics.Batch` for efficient rendering. + """ + self._vertex_list.draw(GL_LINES) + + +class Circle(_ShapeBase): + def __init__(self, x, y, radius, segments=None, color=(255, 255, 255), batch=None, group=None): + """Create a circle. + + The circle's anchor point (x, y) defaults to the center of the circle. + + :Parameters: + `x` : float + X coordinate of the circle. + `y` : float + Y coordinate of the circle. + `radius` : float + The desired radius. + `segments` : int + You can optionally specifify how many distict triangles + the circle should be made from. If not specified, it will + be automatically calculated based on the radius. + `color` : (int, int, int) + The RGB color of the circle, specified as a tuple of + three ints in the range of 0-255. + `batch` : `~pyglet.graphics.Batch` + Optional batch to add the circle to. + `group` : `~pyglet.graphics.Group` + Optional parent group of the circle. + """ + self._x = x + self._y = y + self._radius = radius + self._segments = segments or int(radius / 1.25) + self._rgb = color + + self._batch = batch or Batch() + self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, group) + + self._vertex_list = self._batch.add(self._segments * 3, GL_TRIANGLES, self._group, 'v2f', 'c4B') + self._update_position() + self._update_color() + + def _update_position(self): + if not self._visible: + vertices = (0,) * self._segments * 6 + else: + x = self._x + self._anchor_x + y = self._y + self._anchor_y + r = self._radius + tau_segs = math.pi * 2 / self._segments + + # Calcuate the outer points of the circle: + points = [(x + (r * math.cos(i * tau_segs)), + y + (r * math.sin(i * tau_segs))) for i in range(self._segments)] + + # Create a list of trianges from the points: + vertices = [] + for i, point in enumerate(points): + triangle = x, y, *points[i - 1], *point + vertices.extend(triangle) + + self._vertex_list.vertices[:] = vertices + + def _update_color(self): + self._vertex_list.colors[:] = [*self._rgb, int(self._opacity)] * self._segments * 3 + + @property + def radius(self): + """The radius of the circle. + + :type: float + """ + return self._radius + + @radius.setter + def radius(self, value): + self._radius = value + self._update_position() + + +class Line(_ShapeBase): + def __init__(self, x, y, x2, y2, width=1, color=(255, 255, 255), batch=None, group=None): + """Create a line. + + The line's anchor point defaults to the center of the line's + width on the X axis, and the Y axis. + + :Parameters: + `x` : float + The first X coordinate of the line. + `y` : float + The first Y coordinate of the line. + `x2` : float + The second X coordinate of the line. + `y2` : float + The second Y coordinate of the line. + `width` : float + The desired width of the line. + `color` : (int, int, int) + The RGB color of the line, specified as a tuple of + three ints in the range of 0-255. + `batch` : `~pyglet.graphics.Batch` + Optional batch to add the line to. + `group` : `~pyglet.graphics.Group` + Optional parent group of the line. + """ + self._x = x + self._y = y + self._x2 = x2 + self._y2 = y2 + + self._width = width + self._rotation = math.degrees(math.atan2(y2 - y, x2 - x)) + self._rgb = color + + self._batch = batch or Batch() + self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, group) + self._vertex_list = self._batch.add(6, GL_TRIANGLES, self._group, 'v2f', 'c4B') + self._update_position() + self._update_color() + + def _update_position(self): + if not self._visible: + self._vertex_list.vertices[:] = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + else: + x1 = -self._anchor_y + y1 = self._anchor_x - self._width / 2 + x = self._x + y = self._y + x2 = x1 + math.hypot(self._y2 - y, self._x2 - x) + y2 = y1 + self._width + + r = math.atan2(self._y2 - y, self._x2 - x) + cr = math.cos(r) + sr = math.sin(r) + ax = x1 * cr - y1 * sr + x + ay = x1 * sr + y1 * cr + y + bx = x2 * cr - y1 * sr + x + by = x2 * sr + y1 * cr + y + cx = x2 * cr - y2 * sr + x + cy = x2 * sr + y2 * cr + y + dx = x1 * cr - y2 * sr + x + dy = x1 * sr + y2 * cr + y + self._vertex_list.vertices[:] = (ax, ay, bx, by, cx, cy, ax, ay, cx, cy, dx, dy) + + def _update_color(self): + self._vertex_list.colors[:] = [*self._rgb, int(self._opacity)] * 6 + + @property + def x2(self): + """Second X coordinate of the shape. + + :type: int or float + """ + return self._x2 + + @x2.setter + def x2(self, value): + self._x2 = value + self._update_position() + + @property + def y2(self): + """Second Y coordinate of the shape. + + :type: int or float + """ + return self._y2 + + @y2.setter + def y2(self, value): + self._y2 = value + self._update_position() + + @property + def position(self): + """The (x, y, x2, y2) coordinates of the line, as a tuple. + + :Parameters: + `x` : int or float + X coordinate of the line. + `y` : int or float + Y coordinate of the line. + `x2` : int or float + X2 coordinate of the line. + `y2` : int or float + Y2 coordinate of the line. + """ + return self._x, self._y, self._x2, self._y2 + + @position.setter + def position(self, values): + self._x, self._y, self._x2, self._y2 = values + self._update_position() + + +class Rectangle(_ShapeBase): + def __init__(self, x, y, width, height, color=(255, 255, 255), batch=None, group=None): + """Create a rectangle or square. + + The rectangles's anchor point defaults to the (x, y) coordinates, + which are at the bottom left. + + :Parameters: + `x` : float + The X coordinate of the rectangle. + `y` : float + The Y coordinate of the rectangle. + `width` : float + The width of the rectangle. + `height` : float + The height of the rectangle. + `color` : (int, int, int) + The RGB color of the rectangle, specified as + a tuple of three ints in the range of 0-255. + `batch` : `~pyglet.graphics.Batch` + Optional batch to add the rectangle to. + `group` : `~pyglet.graphics.Group` + Optional parent group of the rectangle. + """ + self._x = x + self._y = y + self._width = width + self._height = height + self._rotation = 0 + self._rgb = color + + self._batch = batch or Batch() + self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, group) + self._vertex_list = self._batch.add(6, GL_TRIANGLES, self._group, 'v2f', 'c4B') + self._update_position() + self._update_color() + + def _update_position(self): + if not self._visible: + self._vertex_list.vertices = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + elif self._rotation: + x1 = -self._anchor_x + y1 = -self._anchor_y + x2 = x1 + self._width + y2 = y1 + self._height + x = self._x + y = self._y + + r = -math.radians(self._rotation) + cr = math.cos(r) + sr = math.sin(r) + ax = x1 * cr - y1 * sr + x + ay = x1 * sr + y1 * cr + y + bx = x2 * cr - y1 * sr + x + by = x2 * sr + y1 * cr + y + cx = x2 * cr - y2 * sr + x + cy = x2 * sr + y2 * cr + y + dx = x1 * cr - y2 * sr + x + dy = x1 * sr + y2 * cr + y + self._vertex_list.vertices = (ax, ay, bx, by, cx, cy, ax, ay, cx, cy, dx, dy) + else: + x1 = self._x - self._anchor_x + y1 = self._y - self._anchor_y + x2 = x1 + self._width + y2 = y1 + self._height + self._vertex_list.vertices = (x1, y1, x2, y1, x2, y2, x1, y1, x2, y2, x1, y2) + + def _update_color(self): + self._vertex_list.colors[:] = [*self._rgb, int(self._opacity)] * 6 + + @property + def width(self): + """The width of the rectangle. + + :type: float + """ + return self._width + + @width.setter + def width(self, value): + self._width = value + self._update_position() + + @property + def height(self): + """The height of the rectangle. + + :type: float + """ + return self._height + + @height.setter + def height(self, value): + self._height = value + self._update_position() + + @property + def rotation(self): + """Clockwise rotation of the rectangle, in degrees. + + The Rectangle will be rotated about its (anchor_x, anchor_y) + position. + + :type: float + """ + return self._rotation + + @rotation.setter + def rotation(self, rotation): + self._rotation = rotation + self._update_position() + + +class BorderedRectangle(_ShapeBase): + def __init__(self, x, y, width, height, border=1, color=(255, 255, 255), + border_color=(100, 100, 100), batch=None, group=None): + """Create a rectangle or square. + + The rectangles's anchor point defaults to the (x, y) coordinates, + which are at the bottom left. + + :Parameters: + `x` : float + The X coordinate of the rectangle. + `y` : float + The Y coordinate of the rectangle. + `width` : float + The width of the rectangle. + `height` : float + The height of the rectangle. + `color` : (int, int, int) + The RGB color of the rectangle, specified as + a tuple of three ints in the range of 0-255. + `batch` : `~pyglet.graphics.Batch` + Optional batch to add the rectangle to. + `group` : `~pyglet.graphics.Group` + Optional parent group of the rectangle. + """ + self._x = x + self._y = y + self._width = width + self._height = height + self._border = border + self._rgb = color + self._brgb = border_color + + self._batch = batch or Batch() + self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, group) + indices = [0, 1, 2, 0, 2, 3, 0, 4, 3, 4, 7, 3, 0, 1, 5, 0, 5, 4, 1, 2, 5, 5, 2, 6, 6, 2, 3, 6, 3, 7] + self._vertex_list = self._batch.add_indexed(8, GL_TRIANGLES, self._group, indices, 'v2f', 'c4B') + self._update_position() + self._update_color() + + def _update_position(self): + if not self._visible: + self._vertex_list.vertices = (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) + else: + b = self._border + bx1 = self._x - self._anchor_x + by1 = self._y - self._anchor_y + bx2 = bx1 + self._width + by2 = by1 + self._height + ix1 = bx1 + b + iy1 = by1 + b + ix2 = bx2 - b + iy2 = by2 - b + self._vertex_list.vertices[:] = (ix1, iy1, ix2, iy1, ix2, iy2, ix1, iy2, + bx1, by1, bx2, by1, bx2, by2, bx1, by2,) + + def _update_color(self): + opacity = int(self._opacity) + self._vertex_list.colors[:] = [*self._rgb, opacity] * 4 + [*self._brgb, opacity] * 4 + + @property + def width(self): + """The width of the rectangle. + + :type: float + """ + return self._width + + @width.setter + def width(self, value): + self._width = value + self._update_position() + + @property + def height(self): + """The height of the rectangle. + + :type: float + """ + return self._height + + @height.setter + def height(self, value): + self._height = value + self._update_position() + + +class Triangle(_ShapeBase): + def __init__(self, x, y, x2, y2, x3, y3, color=(255, 255, 255), batch=None, group=None): + """Create a triangle. + + The triangle's anchor point defaults to the first vertex point. + + :Parameters: + `x` : float + The first X coordinate of the triangle. + `y` : float + The first Y coordinate of the triangle. + `x2` : float + The second X coordinate of the triangle. + `y2` : float + The second Y coordinate of the triangle. + `x3` : float + The third X coordinate of the triangle. + `y3` : float + The third Y coordinate of the triangle. + `color` : (int, int, int) + The RGB color of the triangle, specified as + a tuple of three ints in the range of 0-255. + `batch` : `~pyglet.graphics.Batch` + Optional batch to add the triangle to. + `group` : `~pyglet.graphics.Group` + Optional parent group of the triangle. + """ + self._x = x + self._y = y + self._x2 = x2 + self._y2 = y2 + self._x3 = x3 + self._y3 = y3 + self._rotation = 0 + + self._rgb = color + + self._batch = batch or Batch() + self._group = _ShapeGroup(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, group) + self._vertex_list = self._batch.add(3, GL_TRIANGLES, self._group, 'v2f', 'c4B') + self._update_position() + self._update_color() + + def _update_position(self): + if not self._visible: + self._vertex_list.vertices = (0, 0, 0, 0, 0, 0) + else: + anchor_x = self._anchor_x + anchor_y = self._anchor_y + x1 = self._x - anchor_x + y1 = self._y - anchor_y + x2 = self._x2 - anchor_x + y2 = self._y2 - anchor_y + x3 = self._x3 - anchor_x + y3 = self._y3 - anchor_y + self._vertex_list.vertices = (x1, y1, x2, y2, x3, y3) + + def _update_color(self): + self._vertex_list.colors[:] = [*self._rgb, int(self._opacity)] * 3 + + @property + def x2(self): + """Second X coordinate of the shape. + + :type: int or float + """ + return self._x2 + + @x2.setter + def x2(self, value): + self._x2 = value + self._update_position() + + @property + def y2(self): + """Second Y coordinate of the shape. + + :type: int or float + """ + return self._y2 + + @y2.setter + def y2(self, value): + self._y2 = value + self._update_position() + + @property + def x3(self): + """Third X coordinate of the shape. + + :type: int or float + """ + return self._x3 + + @x3.setter + def x3(self, value): + self._x3 = value + self._update_position() + + @property + def y3(self): + """Third Y coordinate of the shape. + + :type: int or float + """ + return self._y3 + + @y3.setter + def y3(self, value): + self._y3 = value + self._update_position() + + @property + def position(self): + """The (x, y, x2, y2, x3, y3) coordinates of the triangle, as a tuple. + + :Parameters: + `x` : int or float + X coordinate of the triangle. + `y` : int or float + Y coordinate of the triangle. + `x2` : int or float + X2 coordinate of the triangle. + `y2` : int or float + Y2 coordinate of the triangle. + `x3` : int or float + X3 coordinate of the triangle. + `y3` : int or float + Y3 coordinate of the triangle. + """ + return self._x, self._y, self._x2, self._y2, self._x3, self._y3 + + @position.setter + def position(self, values): + self._x, self._y, self._x2, self._y2, self._x3, self._y3 = values + self._update_position() + + +__all__ = ('Arc', 'Circle', 'Line', 'Rectangle', 'BorderedRectangle', 'Triangle') diff -Nru pyglet-1.4.10/pyglet/sprite.py pyglet-1.5.14/pyglet/sprite.py --- pyglet-1.4.10/pyglet/sprite.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/sprite.py 2020-12-23 15:47:40.000000000 +0000 @@ -100,11 +100,8 @@ .. versionadded:: 1.1 """ -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -import math import sys +import math from pyglet.gl import * from pyglet import clock @@ -182,6 +179,8 @@ _batch = None _animation = None + _frame_index = 0 + _paused = False _rotation = 0 _opacity = 255 _rgb = (255, 255, 255) @@ -234,7 +233,6 @@ if isinstance(img, image.Animation): self._animation = img - self._frame_index = 0 self._texture = img.frames[0].image.get_texture() self._next_dt = img.frames[0].duration if self._next_dt: @@ -522,7 +520,7 @@ @property def scale_x(self): """Horizontal scaling factor. - + A scaling factor of 1 (the default) has no effect. A scale of 2 will draw the sprite at twice the native width of its image. @@ -538,7 +536,7 @@ @property def scale_y(self): """Vertical scaling factor. - + A scaling factor of 1 (the default) has no effect. A scale of 2 will draw the sprite at twice the native height of its image. @@ -666,6 +664,51 @@ self._visible = visible self._update_position() + @property + def paused(self): + """Pause/resume the Sprite's Animation + + If `Sprite.image` is an Animation, you can pause or resume + the animation by setting this property to True or False. + If not an Animation, this has no effect. + + :type: bool + """ + return self._paused + + @paused.setter + def paused(self, pause): + if not hasattr(self, '_animation') or pause == self._paused: + return + if pause is True: + clock.unschedule(self._animate) + else: + frame = self._animation.frames[self._frame_index] + self._next_dt = frame.duration + if self._next_dt: + clock.schedule_once(self._animate, self._next_dt) + self._paused = pause + + @property + def frame_index(self): + """The current Animation frame. + + If the `Sprite.image` is an `Animation`, + you can query or set the current frame. + If not an Animation, this will always + be 0. + + :type: int + """ + return self._frame_index + + @frame_index.setter + def frame_index(self, index): + # Bound to available number of frames + if self._animation is None: + return + self._frame_index = max(0, min(index, len(self._animation.frames)-1)) + def draw(self): """Draw the sprite at its current position. diff -Nru pyglet-1.4.10/pyglet/text/caret.py pyglet-1.5.14/pyglet/text/caret.py --- pyglet-1.4.10/pyglet/text/caret.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/caret.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,9 +32,8 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ -'''Provides keyboard and mouse editing procedures for text layout. +"""Provides keyboard and mouse editing procedures for text layout. Example usage:: @@ -47,11 +46,7 @@ my_window.push_handlers(my_caret) .. versionadded:: 1.1 -''' -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' +""" import re import time @@ -60,8 +55,9 @@ from pyglet import event from pyglet.window import key -class Caret(object): - '''Visible text insertion marker for + +class Caret: + """Visible text insertion marker for `pyglet.text.layout.IncrementalTextLayout`. The caret is drawn as a single vertical bar at the document `position` @@ -82,7 +78,7 @@ If the text layout is being used alongside other graphical widgets, a GUI toolkit will be needed to delegate keyboard and mouse events to the appropriate widget. pyglet does not provide such a toolkit at this stage. - ''' + """ _next_word_re = re.compile(r'(?<=\W)\w') _previous_word_re = re.compile(r'(?<=\W)\w+\W*$') @@ -102,10 +98,10 @@ #: Pixels to scroll viewport per mouse scroll wheel movement. Defaults #: to 12pt at 96dpi. - SCROLL_INCREMENT= 12 * 96 // 72 + SCROLL_INCREMENT = 12 * 96 // 72 def __init__(self, layout, batch=None, color=(0, 0, 0)): - '''Create a caret for a layout. + """Create a caret for a layout. By default the layout's batch is used, so the caret does not need to be drawn explicitly. @@ -118,15 +114,14 @@ `color` : (int, int, int) RGB tuple with components in range [0, 255]. - ''' + """ from pyglet import gl self._layout = layout if batch is None: batch = layout.batch r, g, b = color colors = (r, g, b, 255, r, g, b, 255) - self._list = batch.add(2, gl.GL_LINES, layout.background_group, - 'v2f', ('c4B', colors)) + self._list = batch.add(2, gl.GL_LINES, layout.background_group, 'v2f', ('c4B', colors)) self._ideal_x = None self._ideal_line = None @@ -137,10 +132,10 @@ layout.push_handlers(self) def delete(self): - '''Remove the caret from its batch. + """Remove the caret from its batch. Also disconnects the caret from further layout events. - ''' + """ self._list.delete() self._layout.remove_handlers(self) @@ -168,14 +163,13 @@ def _get_visible(self): return self._visible - visible = property(_get_visible, _set_visible, - doc='''Caret visibility. - + visible = property(_get_visible, _set_visible, doc="""Caret visibility. + The caret may be hidden despite this property due to the periodic blinking or by `on_deactivate` if the event handler is attached to a window. :type: bool - ''') + """) def _set_color(self, color): self._list.colors[:3] = color @@ -184,14 +178,13 @@ def _get_color(self): return self._list.colors[:3] - color = property(_get_color, _set_color, - doc='''Caret color. + color = property(_get_color, _set_color, doc="""Caret color. The default caret color is ``[0, 0, 0]`` (black). Each RGB color component is in the range 0 to 255. :type: (int, int, int) - ''') + """) def _set_position(self, index): self._position = index @@ -201,13 +194,13 @@ def _get_position(self): return self._position - position = property(_get_position, _set_position, - doc='''Position of caret within document. + position = property(_get_position, _set_position, doc="""Position of caret within document. :type: int - ''') + """) _mark = None + def _set_mark(self, mark): self._mark = mark self._update(line=self._ideal_line) @@ -218,8 +211,7 @@ return self._mark mark = property(_get_mark, _set_mark, - doc='''Position of immovable end of text selection within - document. + doc="""Position of immovable end of text selection within document. An interactive text selection is determined by its immovable end (the caret's position when a mouse drag begins) and the caret's position, which @@ -228,14 +220,12 @@ This property is ``None`` when there is no selection. :type: int - ''') + """) def _set_line(self, line): if self._ideal_x is None: - self._ideal_x, _ = \ - self._layout.get_point_from_position(self._position) - self._position = \ - self._layout.get_position_on_line(line, self._ideal_x) + self._ideal_x, _ = self._layout.get_point_from_position(self._position) + self._position = self._layout.get_position_on_line(line, self._ideal_x) self._update(line=line, update_ideal_x=False) def _get_line(self): @@ -245,16 +235,16 @@ return self._layout.get_line_from_position(self._position) line = property(_get_line, _set_line, - doc='''Index of line containing the caret's position. + doc="""Index of line containing the caret's position. When set, `position` is modified to place the caret on requested line while maintaining the closest possible X offset. :type: int - ''') + """) def get_style(self, attribute): - '''Get the document's named style at the caret's current position. + """Get the document's named style at the caret's current position. If there is a text selection and the style varies over the selection, `pyglet.text.document.STYLE_INDETERMINATE` is returned. @@ -266,20 +256,19 @@ names. :rtype: object - ''' + """ if self._mark is None or self._mark == self._position: try: return self._next_attributes[attribute] except KeyError: - return self._layout.document.get_style(attribute, - self._position) + return self._layout.document.get_style(attribute, self._position) start = min(self._position, self._mark) end = max(self._position, self._mark) return self._layout.document.get_style_range(attribute, start, end) def set_style(self, attributes): - '''Set the document style at the caret's current position. + """Set the document style at the caret's current position. If there is a text selection the style is modified immediately. Otherwise, the next text that is entered before the position is @@ -291,7 +280,7 @@ `pyglet.text.document` for a list of recognised attribute names. - ''' + """ if self._mark is None or self._mark == self._position: self._next_attributes.update(attributes) @@ -310,7 +299,7 @@ self._layout.set_selection(0, 0) def move_to_point(self, x, y): - '''Move the caret close to the given window coordinate. + """Move the caret close to the given window coordinate. The `mark` will be reset to ``None``. @@ -320,7 +309,7 @@ `y` : int Y coordinate. - ''' + """ line = self._layout.get_line_from_point(x, y) self._mark = None self._layout.set_selection(0, 0) @@ -329,7 +318,7 @@ self._next_attributes.clear() def select_to_point(self, x, y): - '''Move the caret close to the given window coordinate while + """Move the caret close to the given window coordinate while maintaining the `mark`. :Parameters: @@ -338,14 +327,14 @@ `y` : int Y coordinate. - ''' + """ line = self._layout.get_line_from_point(x, y) self._position = self._layout.get_position_on_line(line, x) self._update(line=line) self._next_attributes.clear() def select_word(self, x, y): - '''Select the word at the given window coordinate. + """Select the word at the given window coordinate. :Parameters: `x` : int @@ -353,11 +342,10 @@ `y` : int Y coordinate. - ''' + """ line = self._layout.get_line_from_point(x, y) p = self._layout.get_position_on_line(line, x) - m1 = self._previous_word_re.search(self._layout.document.text, - 0, p+1) + m1 = self._previous_word_re.search(self._layout.document.text, 0, p+1) if not m1: m1 = 0 else: @@ -374,7 +362,7 @@ self._next_attributes.clear() def select_paragraph(self, x, y): - '''Select the paragraph at the given window coordinate. + """Select the paragraph at the given window coordinate. :Parameters: `x` : int @@ -382,7 +370,7 @@ `y` : int Y coordinate. - ''' + """ line = self._layout.get_line_from_point(x, y) p = self._layout.get_position_on_line(line, x) self.mark = self._layout.document.get_paragraph_start(p) @@ -407,7 +395,7 @@ if self._mark is not None: self._layout.set_selection(min(self._position, self._mark), - max(self._position, self._mark)) + max(self._position, self._mark)) self._layout.ensure_line_visible(line) self._layout.ensure_x_visible(x) @@ -418,12 +406,12 @@ self._update() def on_text(self, text): - '''Handler for the `pyglet.window.Window.on_text` event. + """Handler for the `pyglet.window.Window.on_text` event. Caret keyboard handlers assume the layout always has keyboard focus. GUI toolkits should filter keyboard and text events by widget focus before invoking this handler. - ''' + """ if self._mark is not None: self._delete_selection() @@ -435,12 +423,12 @@ return event.EVENT_HANDLED def on_text_motion(self, motion, select=False): - '''Handler for the `pyglet.window.Window.on_text_motion` event. + """Handler for the `pyglet.window.Window.on_text_motion` event. Caret keyboard handlers assume the layout always has keyboard focus. GUI toolkits should filter keyboard and text events by widget focus before invoking this handler. - ''' + """ if motion == key.MOTION_BACKSPACE: if self.mark is not None: self._delete_selection() @@ -474,8 +462,7 @@ elif motion == key.MOTION_END_OF_LINE: line = self.line if line < self._layout.get_line_count() - 1: - self._position = \ - self._layout.get_position_from_line(line + 1) - 1 + self._position = self._layout.get_position_from_line(line + 1) - 1 self._update(line) else: self.position = len(self._layout.document.text) @@ -492,8 +479,7 @@ self.position = m.start() elif motion == key.MOTION_PREVIOUS_WORD: pos = self._position - m = self._previous_word_re.search(self._layout.document.text, - 0, pos) + m = self._previous_word_re.search(self._layout.document.text, 0, pos) if not m: self.position = 0 else: @@ -504,19 +490,19 @@ return event.EVENT_HANDLED def on_text_motion_select(self, motion): - '''Handler for the `pyglet.window.Window.on_text_motion_select` event. + """Handler for the `pyglet.window.Window.on_text_motion_select` event. Caret keyboard handlers assume the layout always has keyboard focus. GUI toolkits should filter keyboard and text events by widget focus before invoking this handler. - ''' + """ if self.mark is None: self.mark = self.position self.on_text_motion(motion, True) return event.EVENT_HANDLED def on_mouse_scroll(self, x, y, scroll_x, scroll_y): - '''Handler for the `pyglet.window.Window.on_mouse_scroll` event. + """Handler for the `pyglet.window.Window.on_mouse_scroll` event. Mouse handlers do not check the bounds of the coordinates: GUI toolkits should filter events that do not intersect the layout @@ -524,13 +510,13 @@ The layout viewport is scrolled by `SCROLL_INCREMENT` pixels per "click". - ''' + """ self._layout.view_x -= scroll_x * self.SCROLL_INCREMENT self._layout.view_y += scroll_y * self.SCROLL_INCREMENT return event.EVENT_HANDLED def on_mouse_press(self, x, y, button, modifiers): - '''Handler for the `pyglet.window.Window.on_mouse_press` event. + """Handler for the `pyglet.window.Window.on_mouse_press` event. Mouse handlers do not check the bounds of the coordinates: GUI toolkits should filter events that do not intersect the layout @@ -542,7 +528,7 @@ technique is not suitable when a GUI toolkit is in use, as the active widget must also be tracked. Do not use this mouse handler if a GUI toolkit is being used. - ''' + """ t = time.time() if t - self._click_time < 0.25: self._click_count += 1 @@ -562,12 +548,12 @@ return event.EVENT_HANDLED def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): - '''Handler for the `pyglet.window.Window.on_mouse_drag` event. + """Handler for the `pyglet.window.Window.on_mouse_drag` event. Mouse handlers do not check the bounds of the coordinates: GUI toolkits should filter events that do not intersect the layout before invoking this handler. - ''' + """ if self.mark is None: self.mark = self.position self.select_to_point(x, y) @@ -575,19 +561,19 @@ return event.EVENT_HANDLED def on_activate(self): - '''Handler for the `pyglet.window.Window.on_activate` event. + """Handler for the `pyglet.window.Window.on_activate` event. The caret is hidden when the window is not active. - ''' + """ self._active = True self.visible = self._active return event.EVENT_HANDLED def on_deactivate(self): - '''Handler for the `pyglet.window.Window.on_deactivate` event. + """Handler for the `pyglet.window.Window.on_deactivate` event. The caret is hidden when the window is not active. - ''' + """ self._active = False self.visible = self._active return event.EVENT_HANDLED diff -Nru pyglet-1.4.10/pyglet/text/document.py pyglet-1.5.14/pyglet/text/document.py --- pyglet-1.4.10/pyglet/text/document.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/document.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id:$ """Formatted and unformatted document interfaces used by text layout. @@ -164,11 +163,6 @@ .. versionadded:: 1.1 """ -from builtins import next -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import re import sys @@ -182,7 +176,7 @@ STYLE_INDETERMINATE = 'indeterminate' -class InlineElement(object): +class InlineElement: """Arbitrary inline element positioned within a formatted document. Elements behave like a single glyph in the document. They are @@ -713,7 +707,7 @@ self.start, self.end, self.value = next(self) -class _FontStyleRunsRangeIterator(object): +class _FontStyleRunsRangeIterator: # XXX subclass runlist def __init__(self, font_names, font_sizes, bolds, italics, dpi): self.zip_iter = runlist.ZipRunIterator( @@ -737,7 +731,7 @@ dpi=self.dpi) -class _NoStyleRangeIterator(object): +class _NoStyleRangeIterator: # XXX subclass runlist def ranges(self, start, end): yield start, end, None diff -Nru pyglet-1.4.10/pyglet/text/formats/attributed.py pyglet-1.5.14/pyglet/text/formats/attributed.py --- pyglet-1.4.10/pyglet/text/formats/attributed.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/formats/attributed.py 2020-12-31 21:03:32.000000000 +0000 @@ -33,21 +33,16 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Extensible attributed text format for representing pyglet formatted +"""Extensible attributed text format for representing pyglet formatted documents. -''' -from builtins import chr -from builtins import map - -from functools import reduce -import operator -import parser +""" + import re -import token +import ast import pyglet -_pattern = re.compile(r''' +_pattern = re.compile(r""" (?P\{\#x(?P[0-9a-fA-F]+)\}) | (?P\{\#(?P[0-9]+)\}) | (?P\{\{) @@ -60,14 +55,17 @@ | (?P\n(?=\S)) | (?P\n\n+) | (?P[^\{\}\n]+) - ''', re.VERBOSE | re.DOTALL) + """, re.VERBOSE | re.DOTALL) + class AttributedTextDecoder(pyglet.text.DocumentDecoder): - def decode(self, text, location=None): - self.doc = pyglet.text.document.FormattedDocument() + def __init__(self): + self.doc = pyglet.text.document.FormattedDocument() self.length = 0 self.attributes = {} + + def decode(self, text, location=None): next_trailing_space = True trailing_newline = True @@ -87,26 +85,18 @@ self.append('\n') trailing_newline = True elif group == 'nl_para': - self.append(m.group('nl_para')[1:]) # ignore the first \n + self.append(m.group('nl_para')[1:]) # ignore the first \n trailing_newline = True elif group == 'attr': - try: - ast = parser.expr(m.group('attr_val')) - if self.safe(ast): - val = eval(ast.compile()) - else: - val = None - except (parser.ParserError, SyntaxError): - val = None + value = ast.literal_eval(m.group('attr_val')) name = m.group('attr_name') if name[0] == '.': if trailing_newline: - self.attributes[name[1:]] = val + self.attributes[name[1:]] = value else: - self.doc.set_paragraph_style(self.length, self.length, - {name[1:]: val}) + self.doc.set_paragraph_style(self.length, self.length, {name[1:]: value}) else: - self.attributes[name] = val + self.attributes[name] = value elif group == 'escape_dec': self.append(chr(int(m.group('escape_dec_val')))) elif group == 'escape_hex': @@ -123,17 +113,3 @@ self.doc.insert_text(self.length, text, self.attributes) self.length += len(text) self.attributes.clear() - - _safe_names = ('True', 'False', 'None') - - def safe(self, ast): - tree = ast.totuple() - return self.safe_node(tree) - - def safe_node(self, node): - if token.ISNONTERMINAL(node[0]): - return reduce(operator.and_, map(self.safe_node, node[1:])) - elif node[0] == token.NAME: - return node[1] in self._safe_names - else: - return True diff -Nru pyglet-1.4.10/pyglet/text/formats/html.py pyglet-1.5.14/pyglet/text/formats/html.py --- pyglet-1.4.10/pyglet/text/formats/html.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/formats/html.py 2020-12-23 15:47:40.000000000 +0000 @@ -33,7 +33,7 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Decode HTML into attributed text. +"""Decode HTML into attributed text. A subset of HTML 4.01 Transitional is implemented. The following elements are supported fully:: @@ -47,41 +47,41 @@ oddly if edited. No CSS styling is supported. -''' -from builtins import chr +""" -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -from future.moves.html.parser import HTMLParser -from future.moves.html import entities import re +from html.parser import HTMLParser +from html import entities + import pyglet from pyglet.text.formats import structured + def _hex_color(val): return [(val >> 16) & 0xff, (val >> 8) & 0xff, val & 0xff, 255] + _color_names = { - 'black': _hex_color(0x000000), - 'silver': _hex_color(0xc0c0c0), - 'gray': _hex_color(0x808080), - 'white': _hex_color(0xffffff), - 'maroon': _hex_color(0x800000), - 'red': _hex_color(0xff0000), - 'purple': _hex_color(0x800080), - 'fucsia': _hex_color(0x008000), - 'green': _hex_color(0x00ff00), - 'lime': _hex_color(0xffff00), - 'olive': _hex_color(0x808000), - 'yellow': _hex_color(0xff0000), - 'navy': _hex_color(0x000080), - 'blue': _hex_color(0x0000ff), - 'teal': _hex_color(0x008080), - 'aqua': _hex_color(0x00ffff), + 'black': _hex_color(0x000000), + 'silver': _hex_color(0xc0c0c0), + 'gray': _hex_color(0x808080), + 'white': _hex_color(0xffffff), + 'maroon': _hex_color(0x800000), + 'red': _hex_color(0xff0000), + 'purple': _hex_color(0x800080), + 'fucsia': _hex_color(0x008000), + 'green': _hex_color(0x00ff00), + 'lime': _hex_color(0xffff00), + 'olive': _hex_color(0x808000), + 'yellow': _hex_color(0xff0000), + 'navy': _hex_color(0x000080), + 'blue': _hex_color(0x0000ff), + 'teal': _hex_color(0x008080), + 'aqua': _hex_color(0x00ffff), } + def _parse_color(value): if value.startswith('#'): return _hex_color(int(value[1:], 16)) @@ -91,20 +91,20 @@ except KeyError: raise ValueError() + _whitespace_re = re.compile(u'[\u0020\u0009\u000c\u200b\r\n]+', re.DOTALL) _metadata_elements = ['head', 'title'] -_block_elements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', - 'ul', 'ol', 'dir', 'menu', - 'pre', 'dl', 'div', 'center', +_block_elements = ['p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', + 'ul', 'ol', 'dir', 'menu', + 'pre', 'dl', 'div', 'center', 'noscript', 'noframes', 'blockquote', 'form', 'isindex', 'hr', 'table', 'fieldset', 'address', - # Incorrect, but we treat list items as blocks: + # Incorrect, but we treat list items as blocks: 'li', 'dd', 'dt', ] - -_block_containers = ['_top_block', +_block_containers = ['_top_block', 'body', 'div', 'center', 'object', 'applet', 'blockquote', 'ins', 'del', 'dd', 'li', 'form', 'fieldset', 'button', 'th', 'td', 'iframe', 'noscript', @@ -114,8 +114,8 @@ class HTMLDecoder(HTMLParser, structured.StructuredTextDecoder): - '''Decoder for HTML documents. - ''' + """Decoder for HTML documents. + """ #: Default style attributes for unstyled text in the HTML document. #: #: :type: dict @@ -209,7 +209,7 @@ style['font_name'] = 'Courier New' elif element == 'u': color = self.current_style.get('color') - if color is None: + if color is None: color = [0, 0, 0, 255] style['underline'] = color elif element == 'font': @@ -353,7 +353,7 @@ def handle_entityref(self, name): if name in entities.name2codepoint: self.handle_data(chr(entities.name2codepoint[name])) - + def handle_charref(self, name): name = name.lower() try: diff -Nru pyglet-1.4.10/pyglet/text/formats/__init__.py pyglet-1.5.14/pyglet/text/formats/__init__.py --- pyglet-1.4.10/pyglet/text/formats/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/formats/__init__.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,10 +32,7 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Document formats. +"""Document formats. .. versionadded:: 1.1 -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' +""" diff -Nru pyglet-1.4.10/pyglet/text/formats/plaintext.py pyglet-1.5.14/pyglet/text/formats/plaintext.py --- pyglet-1.4.10/pyglet/text/formats/plaintext.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/formats/plaintext.py 2020-11-10 10:36:26.000000000 +0000 @@ -33,14 +33,12 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Plain text decoder. -''' - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' +"""Plain text decoder. +""" import pyglet + class PlainTextDecoder(pyglet.text.DocumentDecoder): def decode(self, text, location=None): document = pyglet.text.document.UnformattedDocument() diff -Nru pyglet-1.4.10/pyglet/text/formats/structured.py pyglet-1.5.14/pyglet/text/formats/structured.py --- pyglet-1.4.10/pyglet/text/formats/structured.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/formats/structured.py 2020-12-23 15:47:40.000000000 +0000 @@ -33,19 +33,14 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -'''Base class for structured (hierarchical) document formats. -''' -from __future__ import division -from builtins import range -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' +"""Base class for structured (hierarchical) document formats. +""" import re import pyglet + class ImageElement(pyglet.text.document.InlineElement): def __init__(self, image, width=None, height=None): self.image = image.get_texture() @@ -74,6 +69,7 @@ self.vertex_lists[layout].delete() del self.vertex_lists[layout] + def _int_to_roman(input): # From http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/81611 if not 0 < input < 4000: @@ -87,9 +83,10 @@ input -= ints[i] * count return result -class ListBuilder(object): + +class ListBuilder: def begin(self, decoder, style): - '''Begin a list. + """Begin a list. :Parameters: `decoder` : `StructuredTextDecoder` @@ -97,7 +94,7 @@ `style` : dict Style dictionary that applies over the entire list. - ''' + """ left_margin = decoder.current_style.get('margin_left') or 0 tab_stops = decoder.current_style.get('tab_stops') if tab_stops: @@ -110,7 +107,7 @@ style['tab_stops'] = tab_stops def item(self, decoder, style, value=None): - '''Begin a list item. + """Begin a list item. :Parameters: `decoder` : `StructuredTextDecoder` @@ -121,14 +118,14 @@ Optional value of the list item. The meaning is list-type dependent. - ''' + """ mark = self.get_mark(value) if mark: decoder.add_text(mark) decoder.add_text('\t') def get_mark(self, value=None): - '''Get the mark text for the next list item. + """Get the mark text for the next list item. :Parameters: `value` : str @@ -136,29 +133,30 @@ dependent. :rtype: str - ''' + """ return '' + class UnorderedListBuilder(ListBuilder): def __init__(self, mark): - '''Create an unordered list with constant mark text. + """Create an unordered list with constant mark text. :Parameters: `mark` : str Mark to prepend to each list item. - ''' + """ self.mark = mark - def get_mark(self, value): return self.mark + class OrderedListBuilder(ListBuilder): format_re = re.compile('(.*?)([1aAiI])(.*)') def __init__(self, start, format): - '''Create an ordered list with sequentially numbered mark text. + """Create an ordered list with sequentially numbered mark text. The format is composed of an optional prefix text, a numbering scheme character followed by suffix text. Valid numbering schemes @@ -184,7 +182,7 @@ `format` : str Format style, for example ``"1."``. - ''' + """ self.next_value = start self.prefix, self.numbering, self.suffix = self.format_re.match(format).groups() @@ -213,6 +211,7 @@ else: return '%s%d%s' % (self.prefix, value, self.suffix) + class StructuredTextDecoder(pyglet.text.DocumentDecoder): def decode(self, text, location=None): self.len_text = 0 diff -Nru pyglet-1.4.10/pyglet/text/__init__.py pyglet-1.5.14/pyglet/text/__init__.py --- pyglet-1.4.10/pyglet/text/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id: $ """Text formatting, layout and display. @@ -73,10 +72,6 @@ .. versionadded:: 1.1 """ -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import os.path @@ -89,7 +84,7 @@ pass -class DocumentDecoder(object): +class DocumentDecoder: """Abstract document decoder. """ diff -Nru pyglet-1.4.10/pyglet/text/layout.py pyglet-1.5.14/pyglet/text/layout.py --- pyglet-1.4.10/pyglet/text/layout.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/layout.py 2020-12-23 15:47:40.000000000 +0000 @@ -32,7 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -# $Id: $ """Render simple text and formatted documents efficiently. @@ -153,14 +152,6 @@ .. versionadded:: 1.1 """ -from __future__ import division -from builtins import zip -from builtins import map -from builtins import range -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' import re import sys @@ -209,7 +200,7 @@ assert False, 'Unknown distance unit %s' % unit -class _Line(object): +class _Line: align = 'left' margin_left = 0 @@ -250,7 +241,7 @@ box.delete(layout) -class _LayoutContext(object): +class _LayoutContext: def __init__(self, layout, document, colors_iter, background_iter): self.colors_iter = colors_iter underline_iter = document.get_style_runs('underline') @@ -286,7 +277,7 @@ pass -class _AbstractBox(object): +class _AbstractBox: owner = None def __init__(self, ascent, descent, advance, length): @@ -483,7 +474,7 @@ return '_InlineElementBox(%r)' % self.element -class _InvalidRange(object): +class _InvalidRange: def __init__(self): self.start = sys.maxsize self.end = 0 @@ -720,7 +711,7 @@ self.parent) -class TextLayout(object): +class TextLayout: """Lay out and display documents. This class is intended for displaying documents that do not change @@ -1485,11 +1476,8 @@ self._update() else: dx = x - self._x - l_dx = lambda x: int(x + dx) for vertex_list in self._vertex_lists: - vertices = vertex_list.vertices[:] - vertices[::2] = list(map(l_dx, vertices[::2])) - vertex_list.vertices[:] = vertices + vertex_list.vertices[::2] = [v + dx for v in vertex_list.vertices[::2]] self._x = x def _get_x(self): @@ -1511,11 +1499,8 @@ self._update() else: dy = y - self._y - l_dy = lambda y: int(y + dy) for vertex_list in self._vertex_lists: - vertices = vertex_list.vertices[:] - vertices[1::2] = list(map(l_dy, vertices[1::2])) - vertex_list.vertices[:] = vertices + vertex_list.vertices[1::2] = [v + dy for v in vertex_list.vertices[1::2]] self._y = y def _get_y(self): diff -Nru pyglet-1.4.10/pyglet/text/runlist.py pyglet-1.5.14/pyglet/text/runlist.py --- pyglet-1.4.10/pyglet/text/runlist.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/text/runlist.py 2020-12-23 15:47:40.000000000 +0000 @@ -36,16 +36,9 @@ .. versionadded:: 1.1 """ -from builtins import str -from builtins import zip -from builtins import next -from builtins import object -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - -class _Run(object): +class _Run: def __init__(self, value, count): self.value = value self.count = count @@ -54,7 +47,7 @@ return 'Run(%r, %d)' % (self.value, self.count) -class RunList(object): +class RunList: """List of contiguous runs of values. A `RunList` is an efficient encoding of a sequence of values. For @@ -233,7 +226,7 @@ return str(list(self)) -class AbstractRunIterator(object): +class AbstractRunIterator: """Range iteration over `RunList`. `AbstractRunIterator` objects allow any monotonically non-decreasing diff -Nru pyglet-1.4.10/pyglet/util.py pyglet-1.5.14/pyglet/util.py --- pyglet-1.4.10/pyglet/util.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/pyglet/util.py 2020-12-31 21:03:32.000000000 +0000 @@ -0,0 +1,246 @@ +# ---------------------------------------------------------------------------- +# pyglet +# Copyright (c) 2006-2008 Alex Holkner +# Copyright (c) 2008-2020 pyglet contributors +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in +# the documentation and/or other materials provided with the +# distribution. +# * Neither the name of pyglet nor the names of its +# contributors may be used to endorse or promote products +# derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# ---------------------------------------------------------------------------- + +"""Various utility functions used internally by pyglet +""" + +import os +import sys + +import pyglet + + +def asbytes(s): + if isinstance(s, bytes): + return s + elif isinstance(s, str): + return bytes(ord(c) for c in s) + else: + return bytes(s) + + +def asbytes_filename(s): + if isinstance(s, bytes): + return s + elif isinstance(s, str): + return s.encode(encoding=sys.getfilesystemencoding()) + + +def asstr(s): + if s is None: + return '' + if isinstance(s, str): + return s + return s.decode("utf-8") + + +def with_metaclass(meta, *bases): + """ + Function from jinja2/_compat.py. License: BSD. + Use it like this:: + class BaseForm: + pass + class FormType(type): + pass + class Form(with_metaclass(FormType, BaseForm)): + pass + 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. Because of internal type checks + we also need to make sure that we downgrade the custom metaclass + for one level to something closer to type (that's why __call__ and + __init__ comes back from type etc.). + This has the advantage over six.with_metaclass of not introducing + dummy classes into the final MRO. + """ + class MetaClass(meta): + __call__ = type.__call__ + __init__ = type.__init__ + + def __new__(cls, name, this_bases, d): + if this_bases is None: + return type.__new__(cls, name, (), d) + return meta(name, bases, d) + + return MetaClass('temporary_class', None, {}) + + +def debug_print(enabled_or_option='debug'): + """Get a debug printer that is enabled based on a boolean input or a pyglet option. + The debug print function returned should be used in an assert. This way it can be + optimized out when running python with the -O flag. + + Usage example:: + + from pyglet.debug import debug_print + _debug_media = debug_print('debug_media') + + def some_func(): + assert _debug_media('My debug statement') + + :parameters: + `enabled_or_options` : bool or str + If a bool is passed, debug printing is enabled if it is True. If str is passed + debug printing is enabled if the pyglet option with that name is True. + + :returns: Function for debug printing. + """ + if isinstance(enabled_or_option, bool): + enabled = enabled_or_option + else: + enabled = pyglet.options.get(enabled_or_option, False) + + if enabled: + def _debug_print(*args, **kwargs): + print(*args, **kwargs) + return True + + else: + def _debug_print(*args, **kwargs): + return True + + return _debug_print + + +class Codecs: + """Utility class for handling adding and querying of codecs.""" + + def __init__(self): + self._decoders = [] + self._encoders = [] + self._decoder_extensions = {} # Map str -> list of matching ImageDecoders + self._encoder_extensions = {} # Map str -> list of matching ImageEncoders + + def get_encoders(self, filename=None): + """Get a list of all encoders. If a `filename` is provided, only + encoders supporting that extension will be returned. An empty list + will be return if no encoders for that extension are available. + """ + if filename: + extension = os.path.splitext(filename)[1].lower() + return self._encoder_extensions.get(extension, []) + return self._encoders + + def get_decoders(self, filename=None): + """Get an ordered list of all decoders. If a `filename` is provided, + decoders supporting that extension will be ordered first in the list. + """ + decoders = [] + if filename: + extension = os.path.splitext(filename)[1].lower() + decoders += self._decoder_extensions.get(extension, []) + decoders += [e for e in self._decoders if e not in decoders] + return decoders + + def add_decoders(self, module): + """Add a decoder module. The module must define `get_decoders`. Once + added, the appropriate decoders defined in the codec will be returned by + Codecs.get_decoders. + """ + for decoder in module.get_decoders(): + self._decoders.append(decoder) + for extension in decoder.get_file_extensions(): + if extension not in self._decoder_extensions: + self._decoder_extensions[extension] = [] + self._decoder_extensions[extension].append(decoder) + + def add_encoders(self, module): + """Add an encoder module. The module must define `get_encoders`. Once + added, the appropriate encoders defined in the codec will be returned by + Codecs.get_encoders. + """ + for encoder in module.get_encoders(): + self._encoders.append(encoder) + for extension in encoder.get_file_extensions(): + if extension not in self._encoder_extensions: + self._encoder_extensions[extension] = [] + self._encoder_extensions[extension].append(encoder) + + +class Decoder: + def get_file_extensions(self): + """Return a list or tuple of accepted file extensions, e.g. ['.wav', '.ogg'] + Lower-case only. + """ + raise NotImplementedError() + + def decode(self, *args, **kwargs): + """Read and decode the given file object and return an approprite + pyglet object. Throws DecodeException if there is an error. + `filename` can be a file type hint. + """ + raise NotImplementedError() + + def __hash__(self): + return hash(self.__class__.__name__) + + def __eq__(self, other): + return self.__class__.__name__ == other.__class__.__name__ + + def __repr__(self): + return "{0}{1}".format(self.__class__.__name__, self.get_file_extensions()) + + +class Encoder: + def get_file_extensions(self): + """Return a list or tuple of accepted file extensions, e.g. ['.wav', '.ogg'] + Lower-case only. + """ + raise NotImplementedError() + + def encode(self, media, file, filename): + """Encode the given media type to the given file. `filename` + provides a hint to the file format desired. options are + encoder-specific, and unknown options should be ignored or + issue warnings. + """ + raise NotImplementedError() + + def __hash__(self): + return hash(self.__class__.__name__) + + def __eq__(self, other): + return self.__class__.__name__ == other.__class__.__name__ + + def __repr__(self): + return "{0}{1}".format(self.__class__.__name__, self.get_file_extensions()) + + +class DecodeException(Exception): + exception_priority = 10 + + +class EncodeException(Exception): + pass diff -Nru pyglet-1.4.10/pyglet/window/cocoa/__init__.py pyglet-1.5.14/pyglet/window/cocoa/__init__.py --- pyglet-1.4.10/pyglet/window/cocoa/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/cocoa/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -33,15 +33,6 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -''' -''' -from __future__ import absolute_import -from __future__ import division -from past.utils import old_div - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - from ctypes import * import pyglet @@ -70,12 +61,15 @@ quartz = cocoapy.quartz cf = cocoapy.cf + class CocoaMouseCursor(MouseCursor): - drawable = False + gl_drawable = False + def __init__(self, cursorName): # cursorName is a string identifying one of the named default NSCursors # e.g. 'pointingHandCursor', and can be sent as message to NSCursor class. self.cursorName = cursorName + def set(self): cursor = getattr(NSCursor, self.cursorName)() cursor.set() @@ -218,6 +212,10 @@ if self._maximum_size is not None: self.set_maximum_size(*self._maximum_size) + # TODO: Add support for file drops. + if self._file_drops: + raise NotImplementedError("File drops are not implemented on MacOS") + self.context.update_geometry() self.switch_to() self.set_vsync(self._vsync) @@ -522,9 +520,9 @@ elif isinstance(self._mouse_cursor, CocoaMouseCursor): self._mouse_cursor.set() SystemCursor.unhide() - # If the mouse cursor is drawable, then it we need to hide + # If the mouse cursor is OpenGL drawable, then it we need to hide # the system mouse cursor, so that the cursor can draw itself. - elif self._mouse_cursor.drawable: + elif self._mouse_cursor.gl_drawable: SystemCursor.hide() # Otherwise, show the default cursor. else: diff -Nru pyglet-1.4.10/pyglet/window/cocoa/pyglet_delegate.py pyglet-1.5.14/pyglet/window/cocoa/pyglet_delegate.py --- pyglet-1.4.10/pyglet/window/cocoa/pyglet_delegate.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/cocoa/pyglet_delegate.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,8 +32,6 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from __future__ import absolute_import -from builtins import object from pyglet.libs.darwin.cocoapy import ObjCClass, ObjCSubclass, ObjCInstance from pyglet.libs.darwin.cocoapy import NSApplicationDidHideNotification @@ -47,7 +45,7 @@ NSApplication = ObjCClass('NSApplication') -class PygletDelegate_Implementation(object): +class PygletDelegate_Implementation: PygletDelegate = ObjCSubclass('NSObject', 'PygletDelegate') @PygletDelegate.method(b'@'+PyObjectEncoding) diff -Nru pyglet-1.4.10/pyglet/window/cocoa/pyglet_textview.py pyglet-1.5.14/pyglet/window/cocoa/pyglet_textview.py --- pyglet-1.4.10/pyglet/window/cocoa/pyglet_textview.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/cocoa/pyglet_textview.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,8 +32,7 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from builtins import chr -from builtins import object + import unicodedata from pyglet.window import key @@ -47,7 +46,7 @@ # This custom NSTextView subclass is used for capturing all of the # on_text, on_text_motion, and on_text_motion_select events. -class PygletTextView_Implementation(object): +class PygletTextView_Implementation: PygletTextView = ObjCSubclass('NSTextView', 'PygletTextView') @PygletTextView.method(b'@'+PyObjectEncoding) diff -Nru pyglet-1.4.10/pyglet/window/cocoa/pyglet_view.py pyglet-1.5.14/pyglet/window/cocoa/pyglet_view.py --- pyglet-1.4.10/pyglet/window/cocoa/pyglet_view.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/cocoa/pyglet_view.py 2020-11-16 14:49:52.000000000 +0000 @@ -32,12 +32,12 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from builtins import object from pyglet.window import key, mouse from pyglet.libs.darwin.quartzkey import keymap, charmap from pyglet.libs.darwin import cocoapy + NSTrackingArea = cocoapy.ObjCClass('NSTrackingArea') # Event data helper functions. @@ -46,7 +46,7 @@ def getMouseDelta(nsevent): dx = nsevent.deltaX() dy = -nsevent.deltaY() - return int(round(dx)), int(round(dy)) + return dx, dy def getMousePosition(self, nsevent): @@ -91,7 +91,7 @@ return None -class PygletView_Implementation(object): +class PygletView_Implementation: PygletView = cocoapy.ObjCSubclass('NSView', 'PygletView') @PygletView.method(b'@'+cocoapy.NSRectEncoding+cocoapy.PyObjectEncoding) @@ -166,7 +166,9 @@ # This method is called whenever the view changes size. @PygletView.method(b'v'+cocoapy.NSSizeEncoding) def setFrameSize_(self, size): - cocoapy.send_super(self, 'setFrameSize:', size, argtypes=[cocoapy.NSSize]) + cocoapy.send_super(self, 'setFrameSize:', size, + superclass_name='NSView', + argtypes=[cocoapy.NSSize]) # This method is called when view is first installed as the # contentView of window. Don't do anything on first call. diff -Nru pyglet-1.4.10/pyglet/window/cocoa/pyglet_window.py pyglet-1.5.14/pyglet/window/cocoa/pyglet_window.py --- pyglet-1.4.10/pyglet/window/cocoa/pyglet_window.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/cocoa/pyglet_window.py 2020-12-23 15:47:40.000000000 +0000 @@ -34,14 +34,13 @@ # ---------------------------------------------------------------------------- from ctypes import c_void_p, c_bool -from builtins import object from pyglet.libs.darwin.cocoapy import ObjCClass, ObjCSubclass, send_super from pyglet.libs.darwin.cocoapy import NSUInteger, NSUIntegerEncoding from pyglet.libs.darwin.cocoapy import NSRectEncoding -class PygletWindow_Implementation(object): +class PygletWindow_Implementation: PygletWindow = ObjCSubclass('NSWindow', 'PygletWindow') @PygletWindow.method('B') @@ -69,7 +68,9 @@ app.event_loop.idle() event = send_super(self, 'nextEventMatchingMask:untilDate:inMode:dequeue:', - mask, date, mode, dequeue, argtypes=[NSUInteger, c_void_p, c_void_p, c_bool]) + mask, date, mode, dequeue, + superclass_name='NSWindow', + argtypes=[NSUInteger, c_void_p, c_void_p, c_bool]) if event.value is None: return 0 @@ -82,7 +83,7 @@ return 0.0 -class PygletToolWindow_Implementation(object): +class PygletToolWindow_Implementation: PygletToolWindow = ObjCSubclass('NSPanel', 'PygletToolWindow') @PygletToolWindow.method(b'@'+NSUIntegerEncoding+b'@@B') diff -Nru pyglet-1.4.10/pyglet/window/cocoa/systemcursor.py pyglet-1.5.14/pyglet/window/cocoa/systemcursor.py --- pyglet-1.4.10/pyglet/window/cocoa/systemcursor.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/cocoa/systemcursor.py 2020-11-10 10:36:26.000000000 +0000 @@ -32,20 +32,22 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -from builtins import object from pyglet.libs.darwin import cocoapy + # This class is a wrapper around NSCursor which prevents us from # sending too many hide or unhide messages in a row. Apparently # NSCursor treats them like retain/release messages, which can be # problematic when we are e.g. switching between window & fullscreen. -class SystemCursor(object): +class SystemCursor: cursor_is_hidden = False + @classmethod def hide(cls): if not cls.cursor_is_hidden: cocoapy.send_message('NSCursor', 'hide') cls.cursor_is_hidden = True + @classmethod def unhide(cls): if cls.cursor_is_hidden: diff -Nru pyglet-1.4.10/pyglet/window/event.py pyglet-1.5.14/pyglet/window/event.py --- pyglet-1.4.10/pyglet/window/event.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/event.py 2020-11-10 10:36:26.000000000 +0000 @@ -37,11 +37,6 @@ See :py:class:`~pyglet.window.Window` for a description of the window event types. """ -from __future__ import print_function -from builtins import object - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' import sys @@ -49,7 +44,7 @@ from pyglet.window import mouse -class WindowEventLogger(object): +class WindowEventLogger: """Print all events to a file. When this event handler is added to a window it prints out all events diff -Nru pyglet-1.4.10/pyglet/window/__init__.py pyglet-1.5.14/pyglet/window/__init__.py --- pyglet-1.4.10/pyglet/window/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -122,24 +122,19 @@ win = window.Window(config=configs[0]) """ -from __future__ import division - -from builtins import object - -from future.utils import with_metaclass - -__docformat__ = 'restructuredtext' -__version__ = '$Id$' import sys import math import pyglet +import pyglet.window.key +import pyglet.window.event + from pyglet import gl from pyglet.event import EventDispatcher from pyglet.window import key -import pyglet.window.key -import pyglet.window.event +from pyglet.util import with_metaclass + _is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run @@ -171,12 +166,13 @@ pass -class MouseCursor(object): +class MouseCursor: """An abstract mouse cursor.""" - #: Indicates if the cursor is drawn using OpenGL. This is True - #: for all mouse cursors except system cursors. - drawable = True + #: Indicates if the cursor is drawn + #: using OpenGL, or natively. + gl_drawable = True + hw_drawable = False def draw(self, x, y): """Abstract render method. @@ -193,23 +189,26 @@ Y coordinate of the mouse pointer's hot spot. """ - raise NotImplementedError('abstract') + pass class DefaultMouseCursor(MouseCursor): - """The default mouse cursor #sed by the operating system.""" - drawable = False + """The default mouse cursor set by the operating system.""" + gl_drawable = False + hw_drawable = True class ImageMouseCursor(MouseCursor): """A user-defined mouse cursor created from an image. Use this class to create your own mouse cursors and assign them - to windows. There are no constraints on the image size or format. + to windows. Cursors can be drawn by OpenGL, or optionally passed + to the OS to render natively. There are no restrictions on cursors + drawn by OpenGL, but natively rendered cursors may have some + platform limitations (such as color depth, or size). In general, + reasonably sized cursors will render correctly """ - drawable = True - - def __init__(self, image, hot_x=0, hot_y=0): + def __init__(self, image, hot_x=0, hot_y=0, acceleration=False): """Create a mouse cursor from an image. :Parameters: @@ -218,15 +217,24 @@ valid ``texture`` attribute. `hot_x` : int X coordinate of the "hot" spot in the image relative to the - image's anchor. + image's anchor. May be clamped to the maximum image width + if ``acceleration=True``. `hot_y` : int Y coordinate of the "hot" spot in the image, relative to the - image's anchor. + image's anchor. May be clamped to the maximum image height + if ``acceleration=True``. + `acceleration` : int + If True, draw the cursor natively instead of usign OpenGL. + The image may be downsampled or color reduced to fit the + platform limitations. """ self.texture = image.get_texture() self.hot_x = hot_x self.hot_y = hot_y + self.gl_drawable = not acceleration + self.hw_drawable = acceleration + def draw(self, x, y): gl.glPushAttrib(gl.GL_ENABLE_BIT | gl.GL_CURRENT_BIT) gl.glColor4f(1, 1, 1, 1) @@ -236,7 +244,7 @@ gl.glPopAttrib() -class Projection(object): +class Projection: """Abstract OpenGL projection.""" def set(self, window_width, window_height, viewport_width, viewport_height): @@ -468,6 +476,7 @@ _fullscreen = False _visible = False _vsync = False + _file_drops = False _screen = None _config = None _context = None @@ -503,6 +512,7 @@ fullscreen=False, visible=True, vsync=True, + file_drops=False, display=None, screen=None, config=None, @@ -627,14 +637,10 @@ else: self._vsync = vsync + self._file_drops = file_drops if caption is None: caption = sys.argv[0] - # PYTHON2 - Remove this decode hack for unicode support: - if hasattr(caption, "decode"): - try: - caption = caption.decode("utf8") - except UnicodeDecodeError: - caption = "pyglet" + self._caption = caption from pyglet import app @@ -801,8 +807,7 @@ Override this event handler with your own to create another projection, for example in perspective. """ - viewport_width, viewport_height = self.get_framebuffer_size() - self._projection.set(width, height, viewport_width, viewport_height) + self._projection.set(width, height, *self.get_framebuffer_size()) def on_close(self): """Default on_close handler.""" @@ -854,9 +859,7 @@ """ # Draw mouse cursor if set and visible. # XXX leaves state in modelview regardless of starting state - if (self._mouse_cursor.drawable and - self._mouse_visible and - self._mouse_in_window): + if self._mouse_cursor.gl_drawable and self._mouse_visible and self._mouse_in_window: gl.glMatrixMode(gl.GL_PROJECTION) gl.glPushMatrix() gl.glLoadIdentity() @@ -1554,10 +1557,10 @@ Distance in pixels from the left edge of the window. `y` : int Distance in pixels from the bottom edge of the window. - `scroll_x` : int - Number of "clicks" towards the right (left if negative). - `scroll_y` : int - Number of "clicks" upwards (downwards if negative). + `scroll_x` : float + Amount of movement on the horizontal axis. + `scroll_y` : float + Amount of movement on the vertical axis. :event: """ @@ -1715,6 +1718,15 @@ :event: """ + def on_file_drop(self, x, y, paths): + """File(s) were dropped into the window, will return the position of the cursor and + a list of paths to the files that were dropped. + + .. versionadded:: 1.5.1 + + :event: + """ + def on_draw(self): """The window contents must be redrawn. @@ -1758,10 +1770,11 @@ BaseWindow.register_event_type('on_hide') BaseWindow.register_event_type('on_context_lost') BaseWindow.register_event_type('on_context_state_lost') +BaseWindow.register_event_type('on_file_drop') BaseWindow.register_event_type('on_draw') -class FPSDisplay(object): +class FPSDisplay: """Display of a window's framerate. This is a convenience class to aid in profiling and debugging. Typical @@ -1876,14 +1889,8 @@ elif pyglet.compat_platform in ('win32', 'cygwin'): from pyglet.window.win32 import Win32Window as Window else: - # XXX HACK around circ problem, should be fixed after removal of - # shadow nonsense - #pyglet.window = sys.modules[__name__] - #import key, mouse - from pyglet.window.xlib import XlibWindow as Window -# XXX remove # Create shadow window. (trickery is for circular import) if not _is_pyglet_doc_run: pyglet.window = sys.modules[__name__] diff -Nru pyglet-1.4.10/pyglet/window/key.py pyglet-1.5.14/pyglet/window/key.py --- pyglet-1.4.10/pyglet/window/key.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/key.py 2020-11-10 10:36:26.000000000 +0000 @@ -60,13 +60,9 @@ if modifiers & key.MOD_CTRL: """ -from builtins import str from pyglet import compat_platform -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - class KeyStateHandler(dict): """Simple handler that tracks the state of keys on the keyboard. If a diff -Nru pyglet-1.4.10/pyglet/window/mouse.py pyglet-1.5.14/pyglet/window/mouse.py --- pyglet-1.4.10/pyglet/window/mouse.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/mouse.py 2020-12-23 15:47:40.000000000 +0000 @@ -35,8 +35,6 @@ """Mouse constants and utilities for pyglet.window. """ -__docformat__ = 'restructuredtext' -__version__ = '$Id$' class MouseStateHandler(dict): @@ -45,7 +43,7 @@ For example:: - >>> win = window.Window + >>> win = window.Window() >>> mousebuttons = mouse.MouseStateHandler() >>> win.push_handlers(mousebuttons) @@ -90,7 +88,8 @@ button_names.append('RIGHT') return '|'.join(button_names) + # Symbolic names for the mouse buttons -LEFT = 1 << 0 +LEFT = 1 << 0 MIDDLE = 1 << 1 -RIGHT = 1 << 2 +RIGHT = 1 << 2 diff -Nru pyglet-1.4.10/pyglet/window/win32/__init__.py pyglet-1.5.14/pyglet/window/win32/__init__.py --- pyglet-1.4.10/pyglet/window/win32/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/win32/__init__.py 2020-12-31 21:03:32.000000000 +0000 @@ -32,18 +32,9 @@ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- - -""" -""" -from __future__ import division -from builtins import chr - -__docformat__ = 'restructuredtext' -__version__ = '$Id: $' - from ctypes import * +from functools import lru_cache import unicodedata -import warnings from pyglet import compat_platform @@ -54,37 +45,37 @@ from pyglet.window import BaseWindow, WindowException, MouseCursor from pyglet.window import DefaultMouseCursor, _PlatformEventHandler, _ViewEventHandler from pyglet.event import EventDispatcher -from pyglet.window import key -from pyglet.window import mouse +from pyglet.window import key, mouse from pyglet.canvas.win32 import Win32Canvas -from pyglet.libs.win32 import _user32, _kernel32, _gdi32, _dwmapi +from pyglet.libs.win32 import _user32, _kernel32, _gdi32, _dwmapi, _shell32 from pyglet.libs.win32.constants import * from pyglet.libs.win32.winkey import * from pyglet.libs.win32.types import * # symbol,ctrl -> motion mapping _motion_map = { - (key.UP, False): key.MOTION_UP, - (key.RIGHT, False): key.MOTION_RIGHT, - (key.DOWN, False): key.MOTION_DOWN, - (key.LEFT, False): key.MOTION_LEFT, - (key.RIGHT, True): key.MOTION_NEXT_WORD, - (key.LEFT, True): key.MOTION_PREVIOUS_WORD, - (key.HOME, False): key.MOTION_BEGINNING_OF_LINE, - (key.END, False): key.MOTION_END_OF_LINE, - (key.PAGEUP, False): key.MOTION_PREVIOUS_PAGE, - (key.PAGEDOWN, False): key.MOTION_NEXT_PAGE, - (key.HOME, True): key.MOTION_BEGINNING_OF_FILE, - (key.END, True): key.MOTION_END_OF_FILE, + (key.UP, False): key.MOTION_UP, + (key.RIGHT, False): key.MOTION_RIGHT, + (key.DOWN, False): key.MOTION_DOWN, + (key.LEFT, False): key.MOTION_LEFT, + (key.RIGHT, True): key.MOTION_NEXT_WORD, + (key.LEFT, True): key.MOTION_PREVIOUS_WORD, + (key.HOME, False): key.MOTION_BEGINNING_OF_LINE, + (key.END, False): key.MOTION_END_OF_LINE, + (key.PAGEUP, False): key.MOTION_PREVIOUS_PAGE, + (key.PAGEDOWN, False): key.MOTION_NEXT_PAGE, + (key.HOME, True): key.MOTION_BEGINNING_OF_FILE, + (key.END, True): key.MOTION_END_OF_FILE, (key.BACKSPACE, False): key.MOTION_BACKSPACE, - (key.DELETE, False): key.MOTION_DELETE, + (key.DELETE, False): key.MOTION_DELETE, } class Win32MouseCursor(MouseCursor): - drawable = False + gl_drawable = False + hw_drawable = True def __init__(self, cursor): self.cursor = cursor @@ -97,6 +88,7 @@ Win32EventHandler = _PlatformEventHandler ViewEventHandler = _ViewEventHandler + class Win32Window(BaseWindow): _window_class = None _hwnd = None @@ -114,6 +106,8 @@ _exclusive_mouse_lpos = None _exclusive_mouse_buttons = 0 _mouse_platform_visible = True + + _keyboard_state = {0x02A: False, 0x036: False} # For shift keys. _ws_style = 0 _ex_ws_style = 0 @@ -133,10 +127,10 @@ self._view_event_handlers[message] = func else: self._event_handlers[message] = func - + self._always_dwm = sys.getwindowsversion() >= (6, 2) self._interval = 0 - + super(Win32Window, self).__init__(*args, **kwargs) def _recreate(self, changes): @@ -149,14 +143,14 @@ # Ensure style is set before determining width/height. if self._fullscreen: self._ws_style = WS_POPUP - self._ex_ws_style = 0 # WS_EX_TOPMOST + self._ex_ws_style = 0 # WS_EX_TOPMOST else: styles = { self.WINDOW_STYLE_DEFAULT: (WS_OVERLAPPEDWINDOW, 0), - self.WINDOW_STYLE_DIALOG: (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU, - WS_EX_DLGMODALFRAME), - self.WINDOW_STYLE_TOOL: (WS_OVERLAPPED|WS_CAPTION|WS_SYSMENU, - WS_EX_TOOLWINDOW), + self.WINDOW_STYLE_DIALOG: (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, + WS_EX_DLGMODALFRAME), + self.WINDOW_STYLE_TOOL: (WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU, + WS_EX_TOOLWINDOW), self.WINDOW_STYLE_BORDERLESS: (WS_POPUP, 0), } self._ws_style, self._ex_ws_style = styles[self._style] @@ -164,7 +158,7 @@ if self._resizable and not self._fullscreen: self._ws_style |= WS_THICKFRAME else: - self._ws_style &= ~(WS_THICKFRAME|WS_MAXIMIZEBOX) + self._ws_style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX) if self._fullscreen: width = self.screen.width @@ -231,6 +225,22 @@ 0) self._dc = _user32.GetDC(self._view_hwnd) + + # Only allow files being dropped if specified. + if self._file_drops: + # Allows UAC to not block the drop files request if low permissions. All 3 must be set. + if WINDOWS_7_OR_GREATER: + _user32.ChangeWindowMessageFilterEx(self._hwnd, WM_DROPFILES, MSGFLT_ALLOW, None) + _user32.ChangeWindowMessageFilterEx(self._hwnd, WM_COPYDATA, MSGFLT_ALLOW, None) + _user32.ChangeWindowMessageFilterEx(self._hwnd, WM_COPYGLOBALDATA, MSGFLT_ALLOW, None) + + _shell32.DragAcceptFiles(self._hwnd, True) + + # Register raw input keyboard to allow the window to receive input events. + raw_keyboard = RAWINPUTDEVICE(0x01, 0x06, 0, self._view_hwnd) + if not _user32.RegisterRawInputDevices( + byref(raw_keyboard), 1, sizeof(RAWINPUTDEVICE)): + print("Warning: Failed to register raw input keyboard. on_key events for shift keys will not be called.") else: # Window already exists, update it with new style @@ -239,11 +249,11 @@ _user32.ShowWindow(self._hwnd, SW_HIDE) _user32.SetWindowLongW(self._hwnd, - GWL_STYLE, - self._ws_style) + GWL_STYLE, + self._ws_style) _user32.SetWindowLongW(self._hwnd, - GWL_EXSTYLE, - self._ex_ws_style) + GWL_EXSTYLE, + self._ex_ws_style) if self._fullscreen: hwnd_after = HWND_TOPMOST @@ -253,14 +263,14 @@ # Position and size window if self._fullscreen: _user32.SetWindowPos(self._hwnd, hwnd_after, - self._screen.x, self._screen.y, width, height, SWP_FRAMECHANGED) - elif False: # TODO location not in pyglet API + self._screen.x, self._screen.y, width, height, SWP_FRAMECHANGED) + elif False: # TODO location not in pyglet API x, y = self._client_to_window_pos(*factory.get_location()) _user32.SetWindowPos(self._hwnd, hwnd_after, - x, y, width, height, SWP_FRAMECHANGED) + x, y, width, height, SWP_FRAMECHANGED) else: _user32.SetWindowPos(self._hwnd, hwnd_after, - 0, 0, width, height, SWP_NOMOVE | SWP_FRAMECHANGED) + 0, 0, width, height, SWP_NOMOVE | SWP_FRAMECHANGED) self._update_view_location(self._width, self._height) @@ -288,7 +298,7 @@ else: x = y = 0 _user32.SetWindowPos(self._view_hwnd, 0, - x, y, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER) + x, y, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER) def close(self): if not self._hwnd: @@ -307,7 +317,7 @@ self._dc = None self._wgl_context = None super(Win32Window, self).close() - + def _dwm_composition_enabled(self): """ Checks if Windows DWM is enabled (Windows Vista+) Note: Always on for Windows 8+ @@ -315,15 +325,16 @@ is_enabled = c_int() _dwmapi.DwmIsCompositionEnabled(byref(is_enabled)) return is_enabled.value - + def _get_vsync(self): return bool(self._interval) - vsync = property(_get_vsync) # overrides BaseWindow property + + vsync = property(_get_vsync) # overrides BaseWindow property def set_vsync(self, vsync): if pyglet.options['vsync'] is not None: vsync = pyglet.options['vsync'] - + self._interval = vsync if not self._fullscreen: @@ -338,20 +349,20 @@ def flip(self): self.draw_mouse_cursor() - + if not self._fullscreen: if self._always_dwm or self._dwm_composition_enabled(): if self._interval: _dwmapi.DwmFlush() - + self.context.flip() def set_location(self, x, y): x, y = self._client_to_window_pos(x, y) _user32.SetWindowPos(self._hwnd, 0, x, y, 0, 0, - (SWP_NOZORDER | - SWP_NOSIZE | - SWP_NOOWNERZORDER)) + (SWP_NOZORDER | + SWP_NOSIZE | + SWP_NOOWNERZORDER)) def get_location(self): rect = RECT() @@ -367,14 +378,14 @@ raise WindowException('Cannot set size of fullscreen window.') width, height = self._client_to_window_size(width, height) _user32.SetWindowPos(self._hwnd, 0, 0, 0, width, height, - (SWP_NOZORDER | - SWP_NOMOVE | - SWP_NOOWNERZORDER)) + (SWP_NOZORDER | + SWP_NOMOVE | + SWP_NOOWNERZORDER)) def get_size(self): - #rect = RECT() - #_user32.GetClientRect(self._hwnd, byref(rect)) - #return rect.right - rect.left, rect.bottom - rect.top + # rect = RECT() + # _user32.GetClientRect(self._hwnd, byref(rect)) + # return rect.right - rect.left, rect.bottom - rect.top return self._width, self._height def set_minimum_size(self, width, height): @@ -390,7 +401,7 @@ if visible: insertAfter = HWND_TOPMOST if self._fullscreen else HWND_TOP _user32.SetWindowPos(self._hwnd, insertAfter, 0, 0, 0, 0, - SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW) + SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW) self.dispatch_event('on_resize', self._width, self._height) self.activate() self.dispatch_event('on_show') @@ -414,15 +425,18 @@ if platform_visible is None: platform_visible = (self._mouse_visible and not self._exclusive_mouse and - not self._mouse_cursor.drawable) or \ + (not self._mouse_cursor.gl_drawable or self._mouse_cursor.hw_drawable)) or \ (not self._mouse_in_window or not self._has_focus) - if platform_visible and not self._mouse_cursor.drawable: + if platform_visible and self._mouse_cursor.hw_drawable: if isinstance(self._mouse_cursor, Win32MouseCursor): cursor = self._mouse_cursor.cursor - else: + elif isinstance(self._mouse_cursor, DefaultMouseCursor): cursor = _user32.LoadCursorW(None, MAKEINTRESOURCE(IDC_ARROW)) + else: + cursor = self._create_cursor_from_image(self._mouse_cursor) + _user32.SetClassLongW(self._view_hwnd, GCL_HCURSOR, cursor) _user32.SetCursor(cursor) @@ -439,8 +453,8 @@ self._mouse_platform_visible = platform_visible def _reset_exclusive_mouse_screen(self): - '''Recalculate screen coords of mouse warp point for exclusive - mouse.''' + """Recalculate screen coords of mouse warp point for exclusive + mouse.""" p = POINT() rect = RECT() _user32.GetClientRect(self._view_hwnd, byref(rect)) @@ -454,7 +468,7 @@ def set_exclusive_mouse(self, exclusive=True): if self._exclusive_mouse == exclusive and \ - self._exclusive_mouse_focus == self._has_focus: + self._exclusive_mouse_focus == self._has_focus: return # Mouse: UsagePage = 1, Usage = 2 @@ -503,7 +517,7 @@ def set_exclusive_keyboard(self, exclusive=True): if self._exclusive_keyboard == exclusive and \ - self._exclusive_keyboard_focus == self._has_focus: + self._exclusive_keyboard_focus == self._has_focus: return if exclusive and self._has_focus: @@ -519,24 +533,24 @@ return DefaultMouseCursor() names = { - self.CURSOR_CROSSHAIR: IDC_CROSS, - self.CURSOR_HAND: IDC_HAND, - self.CURSOR_HELP: IDC_HELP, - self.CURSOR_NO: IDC_NO, - self.CURSOR_SIZE: IDC_SIZEALL, - self.CURSOR_SIZE_UP: IDC_SIZENS, - self.CURSOR_SIZE_UP_RIGHT: IDC_SIZENESW, - self.CURSOR_SIZE_RIGHT: IDC_SIZEWE, + self.CURSOR_CROSSHAIR: IDC_CROSS, + self.CURSOR_HAND: IDC_HAND, + self.CURSOR_HELP: IDC_HELP, + self.CURSOR_NO: IDC_NO, + self.CURSOR_SIZE: IDC_SIZEALL, + self.CURSOR_SIZE_UP: IDC_SIZENS, + self.CURSOR_SIZE_UP_RIGHT: IDC_SIZENESW, + self.CURSOR_SIZE_RIGHT: IDC_SIZEWE, self.CURSOR_SIZE_DOWN_RIGHT: IDC_SIZENWSE, - self.CURSOR_SIZE_DOWN: IDC_SIZENS, - self.CURSOR_SIZE_DOWN_LEFT: IDC_SIZENESW, - self.CURSOR_SIZE_LEFT: IDC_SIZEWE, - self.CURSOR_SIZE_UP_LEFT: IDC_SIZENWSE, - self.CURSOR_SIZE_UP_DOWN: IDC_SIZENS, + self.CURSOR_SIZE_DOWN: IDC_SIZENS, + self.CURSOR_SIZE_DOWN_LEFT: IDC_SIZENESW, + self.CURSOR_SIZE_LEFT: IDC_SIZEWE, + self.CURSOR_SIZE_UP_LEFT: IDC_SIZENWSE, + self.CURSOR_SIZE_UP_DOWN: IDC_SIZENS, self.CURSOR_SIZE_LEFT_RIGHT: IDC_SIZEWE, - self.CURSOR_TEXT: IDC_IBEAM, - self.CURSOR_WAIT: IDC_WAIT, - self.CURSOR_WAIT_ARROW: IDC_APPSTARTING, + self.CURSOR_TEXT: IDC_IBEAM, + self.CURSOR_WAIT: IDC_WAIT, + self.CURSOR_WAIT_ARROW: IDC_APPSTARTING, } if name not in names: raise RuntimeError('Unknown cursor name "%s"' % name) @@ -555,7 +569,7 @@ # Exact match always used return img elif img.width >= width and \ - img.width * img.height > image.width * image.height: + img.width * img.height > image.width * image.height: # At least wide enough, and largest area image = img return image @@ -580,7 +594,7 @@ hdc = _user32.GetDC(None) dataptr = c_void_p() bitmap = _gdi32.CreateDIBSection(hdc, byref(header), DIB_RGB_COLORS, - byref(dataptr), None, 0) + byref(dataptr), None, 0) _user32.ReleaseDC(None, hdc) image = image.get_image_data() @@ -612,6 +626,45 @@ icon = get_icon(image) _user32.SetClassLongPtrW(self._hwnd, GCL_HICONSM, icon) + @lru_cache() + def _create_cursor_from_image(self, cursor): + """Creates platform cursor from an ImageCursor instance.""" + fmt = 'BGRA' + image = cursor.texture + pitch = len(fmt) * image.width + + header = BITMAPINFOHEADER() + header.biSize = sizeof(header) + header.biWidth = image.width + header.biHeight = image.height + header.biPlanes = 1 + header.biBitCount = 32 + + hdc = _user32.GetDC(None) + dataptr = c_void_p() + bitmap = _gdi32.CreateDIBSection(hdc, byref(header), DIB_RGB_COLORS, + byref(dataptr), None, 0) + _user32.ReleaseDC(None, hdc) + + image = image.get_image_data() + data = image.get_data(fmt, pitch) + memmove(dataptr, data, len(data)) + + mask = _gdi32.CreateBitmap(image.width, image.height, 1, 1, None) + + iconinfo = ICONINFO() + iconinfo.fIcon = False + iconinfo.hbmMask = mask + iconinfo.hbmColor = bitmap + iconinfo.xHotspot = int(cursor.hot_x) + iconinfo.yHotspot = int(image.height - cursor.hot_y) + icon = _user32.CreateIconIndirect(byref(iconinfo)) + + _gdi32.DeleteObject(mask) + _gdi32.DeleteObject(bitmap) + + return icon + # Private util def _client_to_window_size(self, width, height): @@ -621,7 +674,7 @@ rect.right = width rect.bottom = height _user32.AdjustWindowRectEx(byref(rect), - self._ws_style, False, self._ex_ws_style) + self._ws_style, False, self._ex_ws_style) return rect.right - rect.left, rect.bottom - rect.top def _client_to_window_pos(self, x, y): @@ -629,12 +682,13 @@ rect.left = x rect.top = y _user32.AdjustWindowRectEx(byref(rect), - self._ws_style, False, self._ex_ws_style) + self._ws_style, False, self._ex_ws_style) return rect.left, rect.top # Event dispatching def dispatch_events(self): + """Legacy or manual dispatch.""" from pyglet import app app.platform_event_loop.start() self._allow_dispatch_event = True @@ -647,6 +701,7 @@ self._allow_dispatch_event = False def dispatch_pending_events(self): + """Legacy or manual dispatch.""" while self._event_queue: event = self._event_queue.pop(0) if type(event[0]) is str: @@ -683,11 +738,11 @@ modifiers |= key.MOD_CTRL if _user32.GetKeyState(VK_LWIN) & 0xff00: modifiers |= key.MOD_WINDOWS - if _user32.GetKeyState(VK_CAPITAL) & 0x00ff: # toggle + if _user32.GetKeyState(VK_CAPITAL) & 0x00ff: # toggle modifiers |= key.MOD_CAPSLOCK - if _user32.GetKeyState(VK_NUMLOCK) & 0x00ff: # toggle + if _user32.GetKeyState(VK_NUMLOCK) & 0x00ff: # toggle modifiers |= key.MOD_NUMLOCK - if _user32.GetKeyState(VK_SCROLL) & 0x00ff: # toggle + if _user32.GetKeyState(VK_SCROLL) & 0x00ff: # toggle modifiers |= key.MOD_SCROLLLOCK if key_lParam: if key_lParam & (1 << 29): @@ -726,9 +781,9 @@ symbol = key.RCTRL elif symbol == key.LALT and lParam & (1 << 24): symbol = key.RALT - elif symbol == key.LSHIFT: - pass # TODO: some magic with getstate to find out if it's the - # right or left shift key. + + if wParam == VK_SHIFT: + return # Let raw input handle this instead. modifiers = self._get_modifiers(lParam) @@ -759,16 +814,16 @@ @ViewEventHandler @Win32EventHandler(WM_INPUT) def _event_raw_input(self, msg, wParam, lParam): - if not self._exclusive_mouse: - return 0 - hRawInput = cast(lParam, HRAWINPUT) inp = RAWINPUT() size = UINT(sizeof(inp)) _user32.GetRawInputData(hRawInput, RID_INPUT, byref(inp), - byref(size), sizeof(RAWINPUTHEADER)) + byref(size), sizeof(RAWINPUTHEADER)) if inp.header.dwType == RIM_TYPEMOUSE: + if not self._exclusive_mouse: + return 0 + rmouse = inp.data.mouse if rmouse.usButtonFlags & RI_MOUSE_LEFT_BUTTON_DOWN: @@ -798,7 +853,7 @@ if rmouse.usButtonFlags & RI_MOUSE_WHEEL: delta = SHORT(rmouse.usButtonData).value self.dispatch_event('on_mouse_scroll', - 0, 0, 0, delta / float(WHEEL_DELTA)) + 0, 0, 0, delta / float(WHEEL_DELTA)) if rmouse.usFlags & 0x01 == MOUSE_MOVE_RELATIVE: if rmouse.lLastX != 0 or rmouse.lLastY != 0: @@ -828,8 +883,31 @@ self._get_modifiers()) else: self.dispatch_event('on_mouse_motion', 0, 0, - rel_x, rel_y) + rel_x, rel_y) self._exclusive_mouse_lpos = rmouse.lLastX, rmouse.lLastY + + elif inp.header.dwType == RIM_TYPEKEYBOARD: + if inp.data.keyboard.VKey == 255: + return 0 + + key_up = inp.data.keyboard.Flags & RI_KEY_BREAK + + if inp.data.keyboard.MakeCode == 0x02A: # LEFT_SHIFT + if not key_up and not self._keyboard_state[0x02A]: + self.dispatch_event('on_key_press', key.LSHIFT, self._get_modifiers()) + self._keyboard_state[0x02A] = True + + elif key_up and self._keyboard_state[0x02A]: + self.dispatch_event('on_key_release', key.LSHIFT, self._get_modifiers()) + self._keyboard_state[0x02A] = False + + elif inp.data.keyboard.MakeCode == 0x036: # RIGHT SHIFT + if not key_up and not self._keyboard_state[0x036]: + self.dispatch_event('on_key_press', key.RSHIFT, self._get_modifiers()) + self._keyboard_state[0x036] = True + elif key_up and self._keyboard_state[0x036]: + self.dispatch_event('on_key_release', key.RSHIFT, self._get_modifiers()) + self._keyboard_state[0x036] = False return 0 @@ -880,7 +958,7 @@ # Drag event modifiers = self._get_modifiers() self.dispatch_event('on_mouse_drag', - x, y, dx, dy, buttons, modifiers) + x, y, dx, dy, buttons, modifiers) else: # Motion event self.dispatch_event('on_mouse_motion', x, y, dx, dy) @@ -950,7 +1028,7 @@ def _event_mousewheel(self, msg, wParam, lParam): delta = c_short(wParam >> 16).value self.dispatch_event('on_mouse_scroll', - self._mouse_x, self._mouse_y, 0, delta / float(WHEEL_DELTA)) + self._mouse_x, self._mouse_y, 0, delta / float(WHEEL_DELTA)) return 0 @Win32EventHandler(WM_CLOSE) @@ -1028,7 +1106,7 @@ if app.event_loop is not None: app.event_loop.exit_blocking() - ''' + """ # Alternative to using WM_SETFOCUS and WM_KILLFOCUS. Which # is better? @@ -1040,7 +1118,7 @@ self.dispatch_event('on_activate') _user32.SetFocus(self._hwnd) return 0 - ''' + """ @Win32EventHandler(WM_SETFOCUS) def _event_setfocus(self, msg, wParam, lParam): @@ -1093,3 +1171,30 @@ def _event_erasebkgnd_view(self, msg, wParam, lParam): # Prevent flicker during resize. return 1 + + @Win32EventHandler(WM_DROPFILES) + def _event_drop_files(self, msg, wParam, lParam): + drop = wParam + + # Get the count so we can handle multiple files. + file_count = _shell32.DragQueryFileW(drop, 0xFFFFFFFF, None, 0) + + # Get where drop point was. + point = POINT() + _shell32.DragQueryPoint(drop, ctypes.byref(point)) + + paths = [] + for i in range(file_count): + length = _shell32.DragQueryFileW(drop, i, None, 0) # Length of string. + + buffer = create_unicode_buffer(length+1) + + _shell32.DragQueryFileW(drop, i, buffer, length + 1) + + paths.append(buffer.value) + + _shell32.DragFinish(drop) + + # Reverse Y and call event. + self.dispatch_event('on_file_drop', point.x, self._height - point.y, paths) + return 0 diff -Nru pyglet-1.4.10/pyglet/window/xlib/__init__.py pyglet-1.5.14/pyglet/window/xlib/__init__.py --- pyglet-1.4.10/pyglet/window/xlib/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/pyglet/window/xlib/__init__.py 2020-12-23 15:47:40.000000000 +0000 @@ -33,14 +33,10 @@ # POSSIBILITY OF SUCH DAMAGE. # ---------------------------------------------------------------------------- -__docformat__ = 'restructuredtext' -__version__ = '$Id$' - -from builtins import chr, str - -from ctypes import * import unicodedata -import warnings +import urllib.parse +from ctypes import * +from functools import lru_cache import pyglet from pyglet.window import WindowException, NoSuchDisplayException, MouseCursorException @@ -56,7 +52,7 @@ from pyglet.libs.x11 import xlib from pyglet.libs.x11 import cursorfont -from pyglet.compat import asbytes +from pyglet.util import asbytes try: from pyglet.libs.x11 import xsync @@ -82,6 +78,9 @@ _can_detect_autorepeat = None XA_CARDINAL = 6 # Xatom.h:14 +XA_ATOM = 4 + +XDND_VERSION = 5 # Do we have the November 2000 UTF8 extension? _have_utf8 = hasattr(xlib._lib, 'Xutf8TextListToTextProperty') @@ -112,7 +111,8 @@ class XlibMouseCursor(MouseCursor): - drawable = False + gl_drawable = False + hw_drawable = True def __init__(self, cursor): self.cursor = cursor @@ -123,6 +123,8 @@ ViewEventHandler = _ViewEventHandler + + class XlibWindow(BaseWindow): _x_display = None # X display connection _x_screen_id = None # X screen index @@ -203,6 +205,20 @@ self._create() + def _create_xdnd_atoms(self, display): + self._xdnd_atoms = { + 'XdndAware' : xlib.XInternAtom(display, asbytes('XdndAware'), False), + 'XdndEnter' : xlib.XInternAtom(display, asbytes('XdndEnter'), False), + 'XdndTypeList' : xlib.XInternAtom(display, asbytes('XdndTypeList'), False), + 'XdndDrop' : xlib.XInternAtom(display, asbytes('XdndDrop'), False), + 'XdndFinished' : xlib.XInternAtom(display, asbytes('XdndFinished'), False), + 'XdndSelection' : xlib.XInternAtom(display, asbytes('XdndSelection'), False), + 'XdndPosition' : xlib.XInternAtom(display, asbytes('XdndPosition'), False), + 'XdndStatus' : xlib.XInternAtom(display, asbytes('XdndStatus'), False), + 'XdndActionCopy' : xlib.XInternAtom(display, asbytes('XdndActionCopy'), False), + 'text/uri-list' : xlib.XInternAtom(display, asbytes("text/uri-list"), False) + } + def _create(self): # Unmap existing window if necessary while we fiddle with it. if self._window and self._mapped: @@ -298,6 +314,26 @@ atom, XA_CARDINAL, 32, xlib.PropModeReplace, cast(ptr, POINTER(c_ubyte)), 1) + + # Atoms required for Xdnd + self._create_xdnd_atoms(self._x_display) + + # Support for drag and dropping files needs to be enabled. + if self._file_drops: + # Some variables set because there are 4 different drop events that need shared data. + self._xdnd_source = None + self._xdnd_version = None + self._xdnd_format = None + self._xdnd_position = (0, 0) # For position callback. + + VERSION = c_ulong(int(XDND_VERSION)) + ptr = pointer(VERSION) + + xlib.XChangeProperty(self._x_display, self._window, + self._xdnd_atoms['XdndAware'], XA_ATOM, 32, + xlib.PropModeReplace, + cast(ptr, POINTER(c_ubyte)), 1) + # Set window attributes attributes = xlib.XSetWindowAttributes() attributes_mask = 0 @@ -518,11 +554,11 @@ except UnicodeEncodeError: name = "pyglet" - hints = xlib.XAllocClassHint() - hints.contents.res_class = asbytes(name) - hints.contents.res_name = asbytes(name.lower()) - xlib.XSetClassHint(self._x_display, self._window, hints.contents) - xlib.XFree(hints) + hint = xlib.XAllocClassHint() + hint.contents.res_class = asbytes(name) + hint.contents.res_name = asbytes(name.lower()) + xlib.XSetClassHint(self._x_display, self._window, hint.contents) + xlib.XFree(hint) def get_caption(self): return self._caption @@ -602,23 +638,66 @@ self._set_wm_state('_NET_WM_STATE_MAXIMIZED_HORZ', '_NET_WM_STATE_MAXIMIZED_VERT') + @staticmethod + def _downsample_1bit(pixelarray): + byte_list = [] + value = 0 + + for i, pixel in enumerate(pixelarray): + index = i % 8 + if pixel: + value |= 1 << index + if index == 7: + byte_list.append(value) + value = 0 + + return bytes(byte_list) + + @lru_cache() + def _create_cursor_from_image(self, cursor): + """Creates platform cursor from an ImageCursor instance.""" + image = cursor.texture + width = image.width + height = image.height + + alpha_luma_bytes = image.get_image_data().get_data('AL', -width * 2) + mask_data = self._downsample_1bit(alpha_luma_bytes[0::2]) + bmp_data = self._downsample_1bit(alpha_luma_bytes[1::2]) + + bitmap = xlib.XCreateBitmapFromData(self._x_display, self._window, bmp_data, width, height) + mask = xlib.XCreateBitmapFromData(self._x_display, self._window, mask_data, width, height) + white = xlib.XColor(red=65535, green=65535, blue=65535) # background color + black = xlib.XColor() # foreground color + + # hot_x/y must be within the image dimension, or the cursor will not display: + hot_x = min(max(0, int(self._mouse_cursor.hot_x)), width) + hot_y = min(max(0, int(height - self._mouse_cursor.hot_y)), height) + cursor = xlib.XCreatePixmapCursor(self._x_display, bitmap, mask, white, black, hot_x, hot_y) + xlib.XFreePixmap(self._x_display, bitmap) + xlib.XFreePixmap(self._x_display, mask) + + return cursor + def set_mouse_platform_visible(self, platform_visible=None): if not self._window: return if platform_visible is None: - platform_visible = self._mouse_visible and not self._mouse_cursor.drawable + platform_visible = self._mouse_visible and not self._mouse_cursor.gl_drawable - if not platform_visible: - # Hide pointer by creating an empty cursor - black = xlib.XBlackPixel(self._x_display, self._x_screen_id) + if platform_visible is False: + # Hide pointer by creating an empty cursor: black = xlib.XColor() - bmp = xlib.XCreateBitmapFromData(self._x_display, self._window, c_buffer(8), 8, 8) - cursor = xlib.XCreatePixmapCursor(self._x_display, bmp, bmp, black, black, 0, 0) + bitmap = xlib.XCreateBitmapFromData(self._x_display, self._window, bytes(8), 8, 8) + cursor = xlib.XCreatePixmapCursor(self._x_display, bitmap, bitmap, black, black, 0, 0) xlib.XDefineCursor(self._x_display, self._window, cursor) xlib.XFreeCursor(self._x_display, cursor) - xlib.XFreePixmap(self._x_display, bmp) + xlib.XFreePixmap(self._x_display, bitmap) + elif isinstance(self._mouse_cursor, ImageMouseCursor) and self._mouse_cursor.hw_drawable: + # Create a custom hardware cursor: + cursor = self._create_cursor_from_image(self._mouse_cursor) + xlib.XDefineCursor(self._x_display, self._window, cursor) else: - # Restore cursor + # Restore standard hardware cursor: if isinstance(self._mouse_cursor, XlibMouseCursor): xlib.XDefineCursor(self._x_display, self._window, self._mouse_cursor.cursor) else: @@ -1171,6 +1250,191 @@ hi = ev.xclient.data.l[3] self._current_sync_value = xsync.XSyncValue(hi, lo) + elif ev.xclient.message_type == self._xdnd_atoms['XdndPosition']: + self._event_drag_position(ev) + + elif ev.xclient.message_type == self._xdnd_atoms['XdndDrop']: + self._event_drag_drop(ev) + + elif ev.xclient.message_type == self._xdnd_atoms['XdndEnter']: + self._event_drag_enter(ev) + + def _event_drag_drop(self, ev): + if self._xdnd_version > XDND_VERSION: + return + + time = xlib.CurrentTime + + if self._xdnd_format: + if self._xdnd_version >= 1: + time = ev.xclient.data.l[2] + + # Convert to selection notification. + xlib.XConvertSelection(self._x_display, + self._xdnd_atoms['XdndSelection'], + self._xdnd_format, + self._xdnd_atoms['XdndSelection'], + self._window, + time) + + xlib.XFlush(self._x_display) + + elif self._xdnd_version >= 2: + # If no format send finished with no data. + e = xlib.XEvent() + e.xclient.type = xlib.ClientMessage + e.xclient.message_type = self._xdnd_atoms['XdndFinished'] + e.xclient.display = cast(self._x_display, POINTER(xlib.Display)) + e.xclient.window = self._window + e.xclient.format = 32 + e.xclient.data.l[0] = self._window + e.xclient.data.l[1] = 0 + e.xclient.data.l[2] = None + + xlib.XSendEvent(self._x_display, self._xdnd_source, + False, xlib.NoEventMask, byref(e)) + + xlib.XFlush(self._x_display) + + def _event_drag_position(self, ev): + if self._xdnd_version > XDND_VERSION: + return + + xoff = (ev.xclient.data.l[2] >> 16) & 0xffff + yoff = (ev.xclient.data.l[2]) & 0xffff + + # Need to convert the position to actual window coordinates with the screen offset + child = xlib.Window() + x = c_int() + y = c_int() + xlib.XTranslateCoordinates(self._x_display, + self._get_root(), + self._window, + xoff, yoff, + byref(x), + byref(y), + byref(child)) + + self._xdnd_position = (x.value, y.value) + + e = xlib.XEvent() + e.xclient.type = xlib.ClientMessage + e.xclient.message_type = self._xdnd_atoms['XdndStatus'] + e.xclient.display = cast(self._x_display, POINTER(xlib.Display)) + e.xclient.window = ev.xclient.data.l[0] + e.xclient.format = 32 + e.xclient.data.l[0] = self._window + e.xclient.data.l[2] = 0 + e.xclient.data.l[3] = 0 + + if self._xdnd_format: + e.xclient.data.l[1] = 1 + if self._xdnd_version >= 2: + e.xclient.data.l[4] = self._xdnd_atoms['XdndActionCopy'] + + xlib.XSendEvent(self._x_display, self._xdnd_source, + False, xlib.NoEventMask, byref(e)) + + xlib.XFlush(self._x_display) + + def _event_drag_enter(self, ev): + self._xdnd_source = ev.xclient.data.l[0] + self._xdnd_version = ev.xclient.data.l[1] >> 24 + self._xdnd_format = None + + if self._xdnd_version > XDND_VERSION: + return + + three_or_more = ev.xclient.data.l[1] & 1 + + # Search all of them (usually 8) + if three_or_more: + data, count = self.get_single_property(self._xdnd_source, self._xdnd_atoms['XdndTypeList'], XA_ATOM) + + data = cast(data, POINTER(xlib.Atom)) + else: + # Some old versions may only have 3? Needs testing. + count = 3 + data = ev.xclient.data.l + 2 + + # Check all of the properties we received from the dropped item and verify it support URI. + for i in range(count): + if data[i] == self._xdnd_atoms['text/uri-list']: + self._xdnd_format = self._xdnd_atoms['text/uri-list'] + break + + if data: + xlib.XFree(data) + + def get_single_property(self, window, atom_property, atom_type): + """ Returns the length and data of a window property. """ + actualAtom = xlib.Atom() + actualFormat = c_int() + itemCount = c_ulong() + bytesAfter = c_ulong() + data = POINTER(c_ubyte)() + + xlib.XGetWindowProperty(self._x_display, window, + atom_property, 0, 2147483647, False, atom_type, + byref(actualAtom), + byref(actualFormat), + byref(itemCount), + byref(bytesAfter), + data) + + return data, itemCount.value + + @XlibEventHandler(xlib.SelectionNotify) + def _event_selection_notification(self, ev): + if ev.xselection.property != 0 and ev.xselection.selection == self._xdnd_atoms['XdndSelection']: + if self._xdnd_format: + # This will get the data + data, count = self.get_single_property(ev.xselection.requestor, + ev.xselection.property, + ev.xselection.target) + + buffer = create_string_buffer(count) + memmove(buffer, data, count) + + formatted_paths = self.parse_filenames(buffer.value.decode()) + + e = xlib.XEvent() + e.xclient.type = xlib.ClientMessage + e.xclient.message_type = self._xdnd_atoms['XdndFinished'] + e.xclient.display = cast(self._x_display, POINTER(xlib.Display)) + e.xclient.window = self._window + e.xclient.format = 32 + e.xclient.data.l[0] = self._xdnd_source + e.xclient.data.l[1] = 1 + e.xclient.data.l[2] = self._xdnd_atoms['XdndActionCopy'] + + xlib.XSendEvent(self._x_display, self._get_root(), + False, xlib.NoEventMask, byref(e)) + + xlib.XFlush(self._x_display) + + xlib.XFree(data) + + self.dispatch_event('on_file_drop', self._xdnd_position[0], self._height - self._xdnd_position[1], formatted_paths) + + @staticmethod + def parse_filenames(decoded_string): + """All of the filenames from file drops come as one big string with + some special characters (%20), this will parse them out. + """ + import sys + + different_files = decoded_string.splitlines() + + parsed = [] + for filename in different_files: + if filename: + filename = urllib.parse.urlsplit(filename).path + encoding = sys.getfilesystemencoding() + parsed.append(urllib.parse.unquote(filename, encoding)) + + return parsed + def _sync_resize(self): if self._enable_xsync and self._current_sync_valid: if xsync.XSyncValueIsZero(self._current_sync_value): diff -Nru pyglet-1.4.10/pyglet.egg-info/PKG-INFO pyglet-1.5.14/pyglet.egg-info/PKG-INFO --- pyglet-1.4.10/pyglet.egg-info/PKG-INFO 2020-01-17 18:37:42.000000000 +0000 +++ pyglet-1.5.14/pyglet.egg-info/PKG-INFO 2020-12-31 21:13:06.000000000 +0000 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: pyglet -Version: 1.4.10 +Version: 1.5.14 Summary: Cross-platform windowing and multimedia library Home-page: http://pyglet.readthedocs.org/en/latest/ Author: Alex Holkner @@ -30,7 +30,7 @@ Pyglet has an active developer and user community. If you find a bug or a problem with the documentation, please [open an issue](https://github.com/pyglet/pyglet/issues). - Anyone is welcome to join our [discord] server were a lot of the development discussion is going on. + Anyone is welcome to join our [discord] server where a lot of the development discussion is going on. It's also a great place to ask for help. Some of the features of pyglet are: @@ -41,33 +41,31 @@ * **Take advantage of multiple windows and multi-monitor desktops.** *pyglet* allows you to use multiple platform-native windows, and is fully aware of multi-monitor setups for use with fullscreen games. * **Load images, sound, music and video in almost any format.** *pyglet* can optionally use FFmpeg to play back - audio formats such as MP3, OGG/Vorbis and WMA, and video formats such as DivX, MPEG-2, H.264, WMV and Xvid. + audio formats such as MP3, OGG/Vorbis and WMA, and video formats such as MPEG2, H.264, H.265, WMV and Xvid. Without FFmpeg, *pyglet* contains built-in support for standard formats such as wav, png, bmp, and others. * **pyglet is written entirely in pure Python**, and makes use of the *ctypes* module to interface with system libraries. You can modify the codebase or make a contribution without any second language compilation steps or compiler setup. Despite being pure Python, *pyglet* has excellent performance thanks to advanced batching for - drawing thousands of sprites or animations. + drawing thousands of objects. * **pyglet is provided under the BSD open-source license**, allowing you to use it for both commercial and other open-source projects with very little restriction. ## Requirements - pyglet runs under Python 2.7, and 3.4+. The entire codebase is fully 2/3 dual - compatible, making use of the future module for backwards compatibility with - legacy Python. Being written in pure Python, it also works on other Python + Pyglet runs under Python 3.5+. Being written in pure Python, it also works on other Python interpreters such as PyPy. Supported platforms are: - * Windows XP or later + * Windows 7 or later * Mac OS X 10.3 or later * Linux, with the following libraries (most recent distributions will have these in a default installation): * OpenGL and GLX - * GDK 2.0+ or PIL (required for loading images other than PNG and BMP) + * GDK 2.0+ or Pillow (required for loading images other than PNG and BMP) * OpenAL or Pulseaudio (required for playing audio) - **Please note that pyglet v1.4 will likely be the last version to support - Python 2.7**. Future releases of pyglet will be Python 3 only, and will be - targeting OpenGL 3.3+. Previous releases will remain available for download. + **Please note that pyglet v1.5 will likely be the last version to support + legacy OpenGL**. Future releases of pyglet will be targeting OpenGL 3.3+. + Previous releases will remain available for download. Starting with version 1.4, to play compressed audio and video files, you will also need [FFmpeg](https://ffmpeg.org/). @@ -76,7 +74,7 @@ pyglet is installable from PyPI: - pip install --upgrade pyglet --user + pip install --upgrade --user pyglet ## Installation from source @@ -86,7 +84,7 @@ You can also install the latest development version direct from Github using: - pip install --upgrade https://github.com/pyglet/pyglet/archive/master.zip --user + pip install --upgrade --user https://github.com/pyglet/pyglet/archive/master.zip For local development install pyglet in editable mode: @@ -107,7 +105,7 @@ are going to work with, also read the associated docs. If you don't understand the code with the help of the docs, it is a sign that the docs should be improved. - If you want to contribute to Pyglet, we suggest the following: + If you want to contribute to pyglet, we suggest the following: * Fork the [official repository](https://github.com/pyglet/pyglet/fork). * Apply your changes to your fork. @@ -166,12 +164,11 @@ Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX :: Linux -Classifier: Programming Language :: Python :: 2 -Classifier: Programming Language :: Python :: 2.7 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: Topic :: Games/Entertainment Classifier: Topic :: Software Development :: Libraries :: Python Modules Description-Content-Type: text/markdown diff -Nru pyglet-1.4.10/pyglet.egg-info/SOURCES.txt pyglet-1.5.14/pyglet.egg-info/SOURCES.txt --- pyglet-1.4.10/pyglet.egg-info/SOURCES.txt 2020-01-17 18:37:44.000000000 +0000 +++ pyglet-1.5.14/pyglet.egg-info/SOURCES.txt 2020-12-31 21:13:06.000000000 +0000 @@ -3,33 +3,12 @@ NOTICE README.md RELEASE_NOTES -setup.cfg setup.py -examples/apple_remote.py -examples/apple_remote_demo.py examples/camera.py examples/fixed_resolution.py -examples/font_comparison.py examples/hello_world.py -examples/html_label.py -examples/image_convert.py -examples/image_display.py -examples/input.py -examples/joystick.py -examples/media_info.py -examples/media_player.py examples/multiple_windows.py -examples/opengl.py -examples/opengl_context.py -examples/pyglet.png -examples/show_input.py -examples/synthesizer.py -examples/tablet.py -examples/text_input.py examples/timer.py -examples/video.py -examples/window_events.py -examples/window_platform_event.py examples/3dmodel/logo3d.mtl examples/3dmodel/logo3d.obj examples/3dmodel/model.py @@ -50,6 +29,9 @@ examples/astraea/res/ship.png examples/astraea/res/smoke.png examples/astraea/res/starfield.jpg +examples/events/register_event_type.py +examples/events/window_events.py +examples/events/window_platform_event.py examples/game/resources/asteroid.png examples/game/resources/bullet.png examples/game/resources/bullet.wav @@ -90,10 +72,40 @@ examples/game/version5/game/player.py examples/game/version5/game/resources.py examples/game/version5/game/util.py -examples/noisy/README -examples/noisy/ball.png -examples/noisy/ball.wav -examples/noisy/noisy.py +examples/graphics/image_convert.py +examples/graphics/image_display.py +examples/gui/bar.png +examples/gui/button_down.png +examples/gui/button_hover.png +examples/gui/button_up.png +examples/gui/knob.png +examples/gui/widgets.py +examples/input/apple_remote.py +examples/input/apple_remote_demo.py +examples/input/input.py +examples/input/joystick.py +examples/input/show_input.py +examples/input/tablet.py +examples/media/media_info.py +examples/media/media_player.py +examples/media/synthesizer.py +examples/media/video.py +examples/media/noisy/README +examples/media/noisy/ball.png +examples/media/noisy/ball.wav +examples/media/noisy/noisy.py +examples/media/soundspace/README +examples/media/soundspace/reader.py +examples/media/soundspace/soundspace.py +examples/media/soundspace/res/bass.wav +examples/media/soundspace/res/drums.wav +examples/media/soundspace/res/guitar.wav +examples/media/soundspace/res/piano.wav +examples/media/soundspace/res/space.txt +examples/opengl/opengl.py +examples/opengl/opengl_context.py +examples/opengl/opengl_scissor.py +examples/opengl/pyglet.png examples/programming_guide/animation.py examples/programming_guide/dinosaur.gif examples/programming_guide/events.py @@ -101,24 +113,21 @@ examples/programming_guide/image_viewer.py examples/programming_guide/kitten.jpg examples/programming_guide/window_subclass.py -examples/soundspace/README -examples/soundspace/reader.py -examples/soundspace/soundspace.py -examples/soundspace/res/bass.wav -examples/soundspace/res/drums.wav -examples/soundspace/res/guitar.wav -examples/soundspace/res/piano.wav -examples/soundspace/res/space.txt +examples/text/font_comparison.py +examples/text/html_label.py +examples/text/pyglet.png +examples/text/text_input.py pyglet/__init__.py pyglet/clock.py pyglet/com.py -pyglet/compat.py -pyglet/debug.py pyglet/event.py pyglet/info.py pyglet/lib.py +pyglet/math.py pyglet/resource.py +pyglet/shapes.py pyglet/sprite.py +pyglet/util.py pyglet.egg-info/PKG-INFO pyglet.egg-info/SOURCES.txt pyglet.egg-info/dependency_links.txt @@ -137,237 +146,6 @@ pyglet/canvas/xlib_vidmoderestore.py pyglet/extlibs/__init__.py pyglet/extlibs/png.py -pyglet/extlibs/future/__init__.py -pyglet/extlibs/future/py2/__init__.py -pyglet/extlibs/future/py2/_dummy_thread/__init__.py -pyglet/extlibs/future/py2/_markupbase/__init__.py -pyglet/extlibs/future/py2/_thread/__init__.py -pyglet/extlibs/future/py2/builtins/__init__.py -pyglet/extlibs/future/py2/copyreg/__init__.py -pyglet/extlibs/future/py2/html/__init__.py -pyglet/extlibs/future/py2/html/entities.py -pyglet/extlibs/future/py2/html/parser.py -pyglet/extlibs/future/py2/http/__init__.py -pyglet/extlibs/future/py2/http/client.py -pyglet/extlibs/future/py2/http/cookiejar.py -pyglet/extlibs/future/py2/http/cookies.py -pyglet/extlibs/future/py2/http/server.py -pyglet/extlibs/future/py2/queue/__init__.py -pyglet/extlibs/future/py2/reprlib/__init__.py -pyglet/extlibs/future/py2/socketserver/__init__.py -pyglet/extlibs/future/py2/tkinter/__init__.py -pyglet/extlibs/future/py2/tkinter/colorchooser.py -pyglet/extlibs/future/py2/tkinter/commondialog.py -pyglet/extlibs/future/py2/tkinter/constants.py -pyglet/extlibs/future/py2/tkinter/dialog.py -pyglet/extlibs/future/py2/tkinter/dnd.py -pyglet/extlibs/future/py2/tkinter/filedialog.py -pyglet/extlibs/future/py2/tkinter/font.py -pyglet/extlibs/future/py2/tkinter/messagebox.py -pyglet/extlibs/future/py2/tkinter/scrolledtext.py -pyglet/extlibs/future/py2/tkinter/simpledialog.py -pyglet/extlibs/future/py2/tkinter/tix.py -pyglet/extlibs/future/py2/tkinter/ttk.py -pyglet/extlibs/future/py2/winreg/__init__.py -pyglet/extlibs/future/py2/xmlrpc/__init__.py -pyglet/extlibs/future/py2/xmlrpc/client.py -pyglet/extlibs/future/py2/xmlrpc/server.py -pyglet/extlibs/future/py2_3/__init__.py -pyglet/extlibs/future/py2_3/future/__init__.py -pyglet/extlibs/future/py2_3/future/backports/__init__.py -pyglet/extlibs/future/py2_3/future/backports/_markupbase.py -pyglet/extlibs/future/py2_3/future/backports/datetime.py -pyglet/extlibs/future/py2_3/future/backports/misc.py -pyglet/extlibs/future/py2_3/future/backports/socket.py -pyglet/extlibs/future/py2_3/future/backports/socketserver.py -pyglet/extlibs/future/py2_3/future/backports/total_ordering.py -pyglet/extlibs/future/py2_3/future/backports/email/__init__.py -pyglet/extlibs/future/py2_3/future/backports/email/_encoded_words.py -pyglet/extlibs/future/py2_3/future/backports/email/_header_value_parser.py -pyglet/extlibs/future/py2_3/future/backports/email/_parseaddr.py -pyglet/extlibs/future/py2_3/future/backports/email/_policybase.py -pyglet/extlibs/future/py2_3/future/backports/email/base64mime.py -pyglet/extlibs/future/py2_3/future/backports/email/charset.py -pyglet/extlibs/future/py2_3/future/backports/email/encoders.py -pyglet/extlibs/future/py2_3/future/backports/email/errors.py -pyglet/extlibs/future/py2_3/future/backports/email/feedparser.py -pyglet/extlibs/future/py2_3/future/backports/email/generator.py -pyglet/extlibs/future/py2_3/future/backports/email/header.py -pyglet/extlibs/future/py2_3/future/backports/email/headerregistry.py -pyglet/extlibs/future/py2_3/future/backports/email/iterators.py -pyglet/extlibs/future/py2_3/future/backports/email/message.py -pyglet/extlibs/future/py2_3/future/backports/email/parser.py -pyglet/extlibs/future/py2_3/future/backports/email/policy.py -pyglet/extlibs/future/py2_3/future/backports/email/quoprimime.py -pyglet/extlibs/future/py2_3/future/backports/email/utils.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/__init__.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/application.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/audio.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/base.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/image.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/message.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/multipart.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py -pyglet/extlibs/future/py2_3/future/backports/email/mime/text.py -pyglet/extlibs/future/py2_3/future/backports/html/__init__.py -pyglet/extlibs/future/py2_3/future/backports/html/entities.py -pyglet/extlibs/future/py2_3/future/backports/html/parser.py -pyglet/extlibs/future/py2_3/future/backports/http/__init__.py -pyglet/extlibs/future/py2_3/future/backports/http/client.py -pyglet/extlibs/future/py2_3/future/backports/http/cookiejar.py -pyglet/extlibs/future/py2_3/future/backports/http/cookies.py -pyglet/extlibs/future/py2_3/future/backports/http/server.py -pyglet/extlibs/future/py2_3/future/backports/test/__init__.py -pyglet/extlibs/future/py2_3/future/backports/test/pystone.py -pyglet/extlibs/future/py2_3/future/backports/test/ssl_servers.py -pyglet/extlibs/future/py2_3/future/backports/test/support.py -pyglet/extlibs/future/py2_3/future/backports/urllib/__init__.py -pyglet/extlibs/future/py2_3/future/backports/urllib/error.py -pyglet/extlibs/future/py2_3/future/backports/urllib/parse.py -pyglet/extlibs/future/py2_3/future/backports/urllib/request.py -pyglet/extlibs/future/py2_3/future/backports/urllib/response.py -pyglet/extlibs/future/py2_3/future/backports/urllib/robotparser.py -pyglet/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py -pyglet/extlibs/future/py2_3/future/backports/xmlrpc/client.py -pyglet/extlibs/future/py2_3/future/backports/xmlrpc/server.py -pyglet/extlibs/future/py2_3/future/builtins/__init__.py -pyglet/extlibs/future/py2_3/future/builtins/disabled.py -pyglet/extlibs/future/py2_3/future/builtins/iterators.py -pyglet/extlibs/future/py2_3/future/builtins/misc.py -pyglet/extlibs/future/py2_3/future/builtins/newnext.py -pyglet/extlibs/future/py2_3/future/builtins/newround.py -pyglet/extlibs/future/py2_3/future/builtins/newsuper.py -pyglet/extlibs/future/py2_3/future/moves/__init__.py -pyglet/extlibs/future/py2_3/future/moves/_dummy_thread.py -pyglet/extlibs/future/py2_3/future/moves/_markupbase.py -pyglet/extlibs/future/py2_3/future/moves/_thread.py -pyglet/extlibs/future/py2_3/future/moves/builtins.py -pyglet/extlibs/future/py2_3/future/moves/collections.py -pyglet/extlibs/future/py2_3/future/moves/configparser.py -pyglet/extlibs/future/py2_3/future/moves/copyreg.py -pyglet/extlibs/future/py2_3/future/moves/itertools.py -pyglet/extlibs/future/py2_3/future/moves/pickle.py -pyglet/extlibs/future/py2_3/future/moves/queue.py -pyglet/extlibs/future/py2_3/future/moves/reprlib.py -pyglet/extlibs/future/py2_3/future/moves/socketserver.py -pyglet/extlibs/future/py2_3/future/moves/subprocess.py -pyglet/extlibs/future/py2_3/future/moves/sys.py -pyglet/extlibs/future/py2_3/future/moves/winreg.py -pyglet/extlibs/future/py2_3/future/moves/dbm/__init__.py -pyglet/extlibs/future/py2_3/future/moves/dbm/dumb.py -pyglet/extlibs/future/py2_3/future/moves/dbm/gnu.py -pyglet/extlibs/future/py2_3/future/moves/dbm/ndbm.py -pyglet/extlibs/future/py2_3/future/moves/html/__init__.py -pyglet/extlibs/future/py2_3/future/moves/html/entities.py -pyglet/extlibs/future/py2_3/future/moves/html/parser.py -pyglet/extlibs/future/py2_3/future/moves/http/__init__.py -pyglet/extlibs/future/py2_3/future/moves/http/client.py -pyglet/extlibs/future/py2_3/future/moves/http/cookiejar.py -pyglet/extlibs/future/py2_3/future/moves/http/cookies.py -pyglet/extlibs/future/py2_3/future/moves/http/server.py -pyglet/extlibs/future/py2_3/future/moves/test/__init__.py -pyglet/extlibs/future/py2_3/future/moves/test/support.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/__init__.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/commondialog.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/constants.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/dialog.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/dnd.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/filedialog.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/font.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/messagebox.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/tix.py -pyglet/extlibs/future/py2_3/future/moves/tkinter/ttk.py -pyglet/extlibs/future/py2_3/future/moves/urllib/__init__.py -pyglet/extlibs/future/py2_3/future/moves/urllib/error.py -pyglet/extlibs/future/py2_3/future/moves/urllib/parse.py -pyglet/extlibs/future/py2_3/future/moves/urllib/request.py -pyglet/extlibs/future/py2_3/future/moves/urllib/response.py -pyglet/extlibs/future/py2_3/future/moves/urllib/robotparser.py -pyglet/extlibs/future/py2_3/future/moves/xmlrpc/__init__.py -pyglet/extlibs/future/py2_3/future/moves/xmlrpc/client.py -pyglet/extlibs/future/py2_3/future/moves/xmlrpc/server.py -pyglet/extlibs/future/py2_3/future/standard_library/__init__.py -pyglet/extlibs/future/py2_3/future/tests/__init__.py -pyglet/extlibs/future/py2_3/future/tests/base.py -pyglet/extlibs/future/py2_3/future/types/__init__.py -pyglet/extlibs/future/py2_3/future/types/newbytes.py -pyglet/extlibs/future/py2_3/future/types/newdict.py -pyglet/extlibs/future/py2_3/future/types/newint.py -pyglet/extlibs/future/py2_3/future/types/newlist.py -pyglet/extlibs/future/py2_3/future/types/newmemoryview.py -pyglet/extlibs/future/py2_3/future/types/newobject.py -pyglet/extlibs/future/py2_3/future/types/newopen.py -pyglet/extlibs/future/py2_3/future/types/newrange.py -pyglet/extlibs/future/py2_3/future/types/newstr.py -pyglet/extlibs/future/py2_3/future/utils/__init__.py -pyglet/extlibs/future/py2_3/future/utils/surrogateescape.py -pyglet/extlibs/future/py2_3/libfuturize/__init__.py -pyglet/extlibs/future/py2_3/libfuturize/fixer_util.py -pyglet/extlibs/future/py2_3/libfuturize/main.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/__init__.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_object.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py -pyglet/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py -pyglet/extlibs/future/py2_3/libpasteurize/__init__.py -pyglet/extlibs/future/py2_3/libpasteurize/main.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/__init__.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py -pyglet/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py -pyglet/extlibs/future/py2_3/past/__init__.py -pyglet/extlibs/future/py2_3/past/builtins/__init__.py -pyglet/extlibs/future/py2_3/past/builtins/misc.py -pyglet/extlibs/future/py2_3/past/builtins/noniterators.py -pyglet/extlibs/future/py2_3/past/tests/__init__.py -pyglet/extlibs/future/py2_3/past/translation/__init__.py -pyglet/extlibs/future/py2_3/past/types/__init__.py -pyglet/extlibs/future/py2_3/past/types/basestring.py -pyglet/extlibs/future/py2_3/past/types/olddict.py -pyglet/extlibs/future/py2_3/past/types/oldstr.py -pyglet/extlibs/future/py2_3/past/utils/__init__.py pyglet/font/__init__.py pyglet/font/base.py pyglet/font/fontconfig.py @@ -407,6 +185,10 @@ pyglet/graphics/vertexattribute.py pyglet/graphics/vertexbuffer.py pyglet/graphics/vertexdomain.py +pyglet/gui/__init__.py +pyglet/gui/frame.py +pyglet/gui/ninepatch.py +pyglet/gui/widgets.py pyglet/image/__init__.py pyglet/image/animation.py pyglet/image/atlas.py @@ -420,6 +202,7 @@ pyglet/image/codecs/png.py pyglet/image/codecs/quartz.py pyglet/image/codecs/s3tc.py +pyglet/image/codecs/wic.py pyglet/input/__init__.py pyglet/input/base.py pyglet/input/darwin_hid.py @@ -459,13 +242,18 @@ pyglet/media/codecs/__init__.py pyglet/media/codecs/base.py pyglet/media/codecs/ffmpeg.py +pyglet/media/codecs/gstreamer.py pyglet/media/codecs/wave.py +pyglet/media/codecs/wmf.py pyglet/media/codecs/ffmpeg_lib/__init__.py pyglet/media/codecs/ffmpeg_lib/libavcodec.py pyglet/media/codecs/ffmpeg_lib/libavformat.py pyglet/media/codecs/ffmpeg_lib/libavutil.py pyglet/media/codecs/ffmpeg_lib/libswresample.py pyglet/media/codecs/ffmpeg_lib/libswscale.py +pyglet/media/devices/__init__.py +pyglet/media/devices/base.py +pyglet/media/devices/win32.py pyglet/media/drivers/__init__.py pyglet/media/drivers/base.py pyglet/media/drivers/listener.py @@ -483,6 +271,12 @@ pyglet/media/drivers/pulse/adaptation.py pyglet/media/drivers/pulse/interface.py pyglet/media/drivers/pulse/lib_pulseaudio.py +pyglet/media/drivers/silent/__init__.py +pyglet/media/drivers/silent/adaptation.py +pyglet/media/drivers/xaudio2/__init__.py +pyglet/media/drivers/xaudio2/adaptation.py +pyglet/media/drivers/xaudio2/interface.py +pyglet/media/drivers/xaudio2/lib_xaudio2.py pyglet/model/__init__.py pyglet/model/codecs/__init__.py pyglet/model/codecs/gltf.py @@ -558,9 +352,6 @@ tests/data/images/tests.interactive.test_interactive_test_base._Test.test_1.001.png tests/data/media/README tests/data/media/alert.wav -tests/data/media/alert_pcm_16_11025_1ch.wav -tests/data/media/alert_pcm_16_22050_1ch.wav -tests/data/media/alert_pcm_8_22050_1ch.wav tests/data/media/login.wav tests/data/media/logout.wav tests/data/media/receive.wav @@ -588,253 +379,6 @@ tests/data/media/synthesis_triangle_8_44800_1ch.wav tests/data/models/logo3d.mtl tests/data/models/logo3d.obj -tests/extlibs/__init__.py -tests/extlibs/mock.py -tests/extlibs/future/__init__.py -tests/extlibs/future/py2/__init__.py -tests/extlibs/future/py2/_dummy_thread/__init__.py -tests/extlibs/future/py2/_markupbase/__init__.py -tests/extlibs/future/py2/_thread/__init__.py -tests/extlibs/future/py2/builtins/__init__.py -tests/extlibs/future/py2/copyreg/__init__.py -tests/extlibs/future/py2/html/__init__.py -tests/extlibs/future/py2/html/entities.py -tests/extlibs/future/py2/html/parser.py -tests/extlibs/future/py2/http/__init__.py -tests/extlibs/future/py2/http/client.py -tests/extlibs/future/py2/http/cookiejar.py -tests/extlibs/future/py2/http/cookies.py -tests/extlibs/future/py2/http/server.py -tests/extlibs/future/py2/queue/__init__.py -tests/extlibs/future/py2/reprlib/__init__.py -tests/extlibs/future/py2/socketserver/__init__.py -tests/extlibs/future/py2/tkinter/__init__.py -tests/extlibs/future/py2/tkinter/colorchooser.py -tests/extlibs/future/py2/tkinter/commondialog.py -tests/extlibs/future/py2/tkinter/constants.py -tests/extlibs/future/py2/tkinter/dialog.py -tests/extlibs/future/py2/tkinter/dnd.py -tests/extlibs/future/py2/tkinter/filedialog.py -tests/extlibs/future/py2/tkinter/font.py -tests/extlibs/future/py2/tkinter/messagebox.py -tests/extlibs/future/py2/tkinter/scrolledtext.py -tests/extlibs/future/py2/tkinter/simpledialog.py -tests/extlibs/future/py2/tkinter/tix.py -tests/extlibs/future/py2/tkinter/ttk.py -tests/extlibs/future/py2/winreg/__init__.py -tests/extlibs/future/py2/xmlrpc/__init__.py -tests/extlibs/future/py2/xmlrpc/client.py -tests/extlibs/future/py2/xmlrpc/server.py -tests/extlibs/future/py2_3/__init__.py -tests/extlibs/future/py2_3/future/__init__.py -tests/extlibs/future/py2_3/future/backports/__init__.py -tests/extlibs/future/py2_3/future/backports/_markupbase.py -tests/extlibs/future/py2_3/future/backports/datetime.py -tests/extlibs/future/py2_3/future/backports/misc.py -tests/extlibs/future/py2_3/future/backports/socket.py -tests/extlibs/future/py2_3/future/backports/socketserver.py -tests/extlibs/future/py2_3/future/backports/total_ordering.py -tests/extlibs/future/py2_3/future/backports/email/__init__.py -tests/extlibs/future/py2_3/future/backports/email/_encoded_words.py -tests/extlibs/future/py2_3/future/backports/email/_header_value_parser.py -tests/extlibs/future/py2_3/future/backports/email/_parseaddr.py -tests/extlibs/future/py2_3/future/backports/email/_policybase.py -tests/extlibs/future/py2_3/future/backports/email/base64mime.py -tests/extlibs/future/py2_3/future/backports/email/charset.py -tests/extlibs/future/py2_3/future/backports/email/encoders.py -tests/extlibs/future/py2_3/future/backports/email/errors.py -tests/extlibs/future/py2_3/future/backports/email/feedparser.py -tests/extlibs/future/py2_3/future/backports/email/generator.py -tests/extlibs/future/py2_3/future/backports/email/header.py -tests/extlibs/future/py2_3/future/backports/email/headerregistry.py -tests/extlibs/future/py2_3/future/backports/email/iterators.py -tests/extlibs/future/py2_3/future/backports/email/message.py -tests/extlibs/future/py2_3/future/backports/email/parser.py -tests/extlibs/future/py2_3/future/backports/email/policy.py -tests/extlibs/future/py2_3/future/backports/email/quoprimime.py -tests/extlibs/future/py2_3/future/backports/email/utils.py -tests/extlibs/future/py2_3/future/backports/email/mime/__init__.py -tests/extlibs/future/py2_3/future/backports/email/mime/application.py -tests/extlibs/future/py2_3/future/backports/email/mime/audio.py -tests/extlibs/future/py2_3/future/backports/email/mime/base.py -tests/extlibs/future/py2_3/future/backports/email/mime/image.py -tests/extlibs/future/py2_3/future/backports/email/mime/message.py -tests/extlibs/future/py2_3/future/backports/email/mime/multipart.py -tests/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py -tests/extlibs/future/py2_3/future/backports/email/mime/text.py -tests/extlibs/future/py2_3/future/backports/html/__init__.py -tests/extlibs/future/py2_3/future/backports/html/entities.py -tests/extlibs/future/py2_3/future/backports/html/parser.py -tests/extlibs/future/py2_3/future/backports/http/__init__.py -tests/extlibs/future/py2_3/future/backports/http/client.py -tests/extlibs/future/py2_3/future/backports/http/cookiejar.py -tests/extlibs/future/py2_3/future/backports/http/cookies.py -tests/extlibs/future/py2_3/future/backports/http/server.py -tests/extlibs/future/py2_3/future/backports/test/__init__.py -tests/extlibs/future/py2_3/future/backports/test/badcert.pem -tests/extlibs/future/py2_3/future/backports/test/badkey.pem -tests/extlibs/future/py2_3/future/backports/test/dh512.pem -tests/extlibs/future/py2_3/future/backports/test/https_svn_python_org_root.pem -tests/extlibs/future/py2_3/future/backports/test/keycert.passwd.pem -tests/extlibs/future/py2_3/future/backports/test/keycert.pem -tests/extlibs/future/py2_3/future/backports/test/keycert2.pem -tests/extlibs/future/py2_3/future/backports/test/nokia.pem -tests/extlibs/future/py2_3/future/backports/test/nullbytecert.pem -tests/extlibs/future/py2_3/future/backports/test/nullcert.pem -tests/extlibs/future/py2_3/future/backports/test/pystone.py -tests/extlibs/future/py2_3/future/backports/test/sha256.pem -tests/extlibs/future/py2_3/future/backports/test/ssl_cert.pem -tests/extlibs/future/py2_3/future/backports/test/ssl_key.passwd.pem -tests/extlibs/future/py2_3/future/backports/test/ssl_key.pem -tests/extlibs/future/py2_3/future/backports/test/ssl_servers.py -tests/extlibs/future/py2_3/future/backports/test/support.py -tests/extlibs/future/py2_3/future/backports/urllib/__init__.py -tests/extlibs/future/py2_3/future/backports/urllib/error.py -tests/extlibs/future/py2_3/future/backports/urllib/parse.py -tests/extlibs/future/py2_3/future/backports/urllib/request.py -tests/extlibs/future/py2_3/future/backports/urllib/response.py -tests/extlibs/future/py2_3/future/backports/urllib/robotparser.py -tests/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py -tests/extlibs/future/py2_3/future/backports/xmlrpc/client.py -tests/extlibs/future/py2_3/future/backports/xmlrpc/server.py -tests/extlibs/future/py2_3/future/builtins/__init__.py -tests/extlibs/future/py2_3/future/builtins/disabled.py -tests/extlibs/future/py2_3/future/builtins/iterators.py -tests/extlibs/future/py2_3/future/builtins/misc.py -tests/extlibs/future/py2_3/future/builtins/newnext.py -tests/extlibs/future/py2_3/future/builtins/newround.py -tests/extlibs/future/py2_3/future/builtins/newsuper.py -tests/extlibs/future/py2_3/future/moves/__init__.py -tests/extlibs/future/py2_3/future/moves/_dummy_thread.py -tests/extlibs/future/py2_3/future/moves/_markupbase.py -tests/extlibs/future/py2_3/future/moves/_thread.py -tests/extlibs/future/py2_3/future/moves/builtins.py -tests/extlibs/future/py2_3/future/moves/collections.py -tests/extlibs/future/py2_3/future/moves/configparser.py -tests/extlibs/future/py2_3/future/moves/copyreg.py -tests/extlibs/future/py2_3/future/moves/itertools.py -tests/extlibs/future/py2_3/future/moves/pickle.py -tests/extlibs/future/py2_3/future/moves/queue.py -tests/extlibs/future/py2_3/future/moves/reprlib.py -tests/extlibs/future/py2_3/future/moves/socketserver.py -tests/extlibs/future/py2_3/future/moves/subprocess.py -tests/extlibs/future/py2_3/future/moves/sys.py -tests/extlibs/future/py2_3/future/moves/winreg.py -tests/extlibs/future/py2_3/future/moves/dbm/__init__.py -tests/extlibs/future/py2_3/future/moves/dbm/dumb.py -tests/extlibs/future/py2_3/future/moves/dbm/gnu.py -tests/extlibs/future/py2_3/future/moves/dbm/ndbm.py -tests/extlibs/future/py2_3/future/moves/html/__init__.py -tests/extlibs/future/py2_3/future/moves/html/entities.py -tests/extlibs/future/py2_3/future/moves/html/parser.py -tests/extlibs/future/py2_3/future/moves/http/__init__.py -tests/extlibs/future/py2_3/future/moves/http/client.py -tests/extlibs/future/py2_3/future/moves/http/cookiejar.py -tests/extlibs/future/py2_3/future/moves/http/cookies.py -tests/extlibs/future/py2_3/future/moves/http/server.py -tests/extlibs/future/py2_3/future/moves/test/__init__.py -tests/extlibs/future/py2_3/future/moves/test/support.py -tests/extlibs/future/py2_3/future/moves/tkinter/__init__.py -tests/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py -tests/extlibs/future/py2_3/future/moves/tkinter/commondialog.py -tests/extlibs/future/py2_3/future/moves/tkinter/constants.py -tests/extlibs/future/py2_3/future/moves/tkinter/dialog.py -tests/extlibs/future/py2_3/future/moves/tkinter/dnd.py -tests/extlibs/future/py2_3/future/moves/tkinter/filedialog.py -tests/extlibs/future/py2_3/future/moves/tkinter/font.py -tests/extlibs/future/py2_3/future/moves/tkinter/messagebox.py -tests/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py -tests/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py -tests/extlibs/future/py2_3/future/moves/tkinter/tix.py -tests/extlibs/future/py2_3/future/moves/tkinter/ttk.py -tests/extlibs/future/py2_3/future/moves/urllib/__init__.py -tests/extlibs/future/py2_3/future/moves/urllib/error.py -tests/extlibs/future/py2_3/future/moves/urllib/parse.py -tests/extlibs/future/py2_3/future/moves/urllib/request.py -tests/extlibs/future/py2_3/future/moves/urllib/response.py -tests/extlibs/future/py2_3/future/moves/urllib/robotparser.py -tests/extlibs/future/py2_3/future/moves/xmlrpc/__init__.py -tests/extlibs/future/py2_3/future/moves/xmlrpc/client.py -tests/extlibs/future/py2_3/future/moves/xmlrpc/server.py -tests/extlibs/future/py2_3/future/standard_library/__init__.py -tests/extlibs/future/py2_3/future/tests/__init__.py -tests/extlibs/future/py2_3/future/tests/base.py -tests/extlibs/future/py2_3/future/types/__init__.py -tests/extlibs/future/py2_3/future/types/newbytes.py -tests/extlibs/future/py2_3/future/types/newdict.py -tests/extlibs/future/py2_3/future/types/newint.py -tests/extlibs/future/py2_3/future/types/newlist.py -tests/extlibs/future/py2_3/future/types/newmemoryview.py -tests/extlibs/future/py2_3/future/types/newobject.py -tests/extlibs/future/py2_3/future/types/newopen.py -tests/extlibs/future/py2_3/future/types/newrange.py -tests/extlibs/future/py2_3/future/types/newstr.py -tests/extlibs/future/py2_3/future/utils/__init__.py -tests/extlibs/future/py2_3/future/utils/surrogateescape.py -tests/extlibs/future/py2_3/libfuturize/__init__.py -tests/extlibs/future/py2_3/libfuturize/fixer_util.py -tests/extlibs/future/py2_3/libfuturize/main.py -tests/extlibs/future/py2_3/libfuturize/fixes/__init__.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_division.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_object.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_print.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py -tests/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py -tests/extlibs/future/py2_3/libpasteurize/__init__.py -tests/extlibs/future/py2_3/libpasteurize/main.py -tests/extlibs/future/py2_3/libpasteurize/fixes/__init__.py -tests/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py -tests/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py -tests/extlibs/future/py2_3/past/__init__.py -tests/extlibs/future/py2_3/past/builtins/__init__.py -tests/extlibs/future/py2_3/past/builtins/misc.py -tests/extlibs/future/py2_3/past/builtins/noniterators.py -tests/extlibs/future/py2_3/past/tests/__init__.py -tests/extlibs/future/py2_3/past/translation/__init__.py -tests/extlibs/future/py2_3/past/types/__init__.py -tests/extlibs/future/py2_3/past/types/basestring.py -tests/extlibs/future/py2_3/past/types/olddict.py -tests/extlibs/future/py2_3/past/types/oldstr.py -tests/extlibs/future/py2_3/past/utils/__init__.py tests/integration/__init__.py tests/integration/test_toplevel_imports.py tests/integration/app/__init__.py @@ -853,7 +397,6 @@ tests/integration/image/test_gdkpixbuf2.py tests/integration/image/test_imagegrid.py tests/integration/image/test_texture3d.py -tests/integration/image/texture_compat.py tests/integration/media/__init__.py tests/integration/media/mock_player.py tests/integration/media/test_directsound.py @@ -891,43 +434,7 @@ tests/interactive/media/__init__.py tests/interactive/media/test_player.py tests/interactive/screenshots/committed/README -tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.001.png -tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.002.png -tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.003.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_16bpp.bmp].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_1bpp.bmp].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_24bpp.bmp].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_32bpp.bmp].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_4bpp.bmp].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_8bpp.bmp].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgba_32bpp.bmp].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_buffer_copy.001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_buffer_saving.001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_checkerboard.001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgb_dxt1.dds].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt1.dds].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt3.dds].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt5.dds].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_depth_buffer_saving.001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[8bpp.gif].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[l.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[la.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgb.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgba.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[l.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[la.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp_trans.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgba.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[l.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[la.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[rgb.png].001.png -tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[rgba.png].001.png tests/interactive/screenshots/session/README -tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.001.png -tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.002.png -tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.003.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_16bpp.bmp].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_1bpp.bmp].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_24bpp.bmp].001.png @@ -935,29 +442,23 @@ tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_4bpp.bmp].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_8bpp.bmp].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgba_32bpp.bmp].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_buffer_copy.001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_buffer_saving.001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_checkerboard.001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgb_dxt1.dds].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt1.dds].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt3.dds].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt5.dds].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_depth_buffer_saving.001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[8bpp.gif].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[l.png].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[la.png].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgb.png].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgba.png].001.png +tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[l.png].001.png +tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[la.png].001.png +tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[rgb.png].001.png +tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[rgba.png].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[l.png].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[la.png].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb.png].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp.png].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp_trans.png].001.png tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgba.png].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[l.png].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[la.png].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[rgb.png].001.png -tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[rgba.png].001.png tests/interactive/text/__init__.py tests/interactive/text/test_content_valign.py tests/interactive/text/test_html.py @@ -981,12 +482,12 @@ tests/unit/test_clock_fps.py tests/unit/test_events.py tests/unit/test_font.py +tests/unit/test_math.py tests/unit/test_resource_path.py tests/unit/test_text.py tests/unit/media/__init__.py tests/unit/media/test_listener.py tests/unit/media/test_player.py -tests/unit/media/test_riff.py tests/unit/media/test_sources.py tests/unit/media/test_synthesis.py tests/unit/text/__init__.py diff -Nru pyglet-1.4.10/README.md pyglet-1.5.14/README.md --- pyglet-1.4.10/README.md 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/README.md 2020-12-23 15:47:40.000000000 +0000 @@ -18,7 +18,7 @@ Pyglet has an active developer and user community. If you find a bug or a problem with the documentation, please [open an issue](https://github.com/pyglet/pyglet/issues). -Anyone is welcome to join our [discord] server were a lot of the development discussion is going on. +Anyone is welcome to join our [discord] server where a lot of the development discussion is going on. It's also a great place to ask for help. Some of the features of pyglet are: @@ -29,33 +29,31 @@ * **Take advantage of multiple windows and multi-monitor desktops.** *pyglet* allows you to use multiple platform-native windows, and is fully aware of multi-monitor setups for use with fullscreen games. * **Load images, sound, music and video in almost any format.** *pyglet* can optionally use FFmpeg to play back - audio formats such as MP3, OGG/Vorbis and WMA, and video formats such as DivX, MPEG-2, H.264, WMV and Xvid. + audio formats such as MP3, OGG/Vorbis and WMA, and video formats such as MPEG2, H.264, H.265, WMV and Xvid. Without FFmpeg, *pyglet* contains built-in support for standard formats such as wav, png, bmp, and others. * **pyglet is written entirely in pure Python**, and makes use of the *ctypes* module to interface with system libraries. You can modify the codebase or make a contribution without any second language compilation steps or compiler setup. Despite being pure Python, *pyglet* has excellent performance thanks to advanced batching for - drawing thousands of sprites or animations. + drawing thousands of objects. * **pyglet is provided under the BSD open-source license**, allowing you to use it for both commercial and other open-source projects with very little restriction. ## Requirements -pyglet runs under Python 2.7, and 3.4+. The entire codebase is fully 2/3 dual -compatible, making use of the future module for backwards compatibility with -legacy Python. Being written in pure Python, it also works on other Python +Pyglet runs under Python 3.5+. Being written in pure Python, it also works on other Python interpreters such as PyPy. Supported platforms are: -* Windows XP or later +* Windows 7 or later * Mac OS X 10.3 or later * Linux, with the following libraries (most recent distributions will have these in a default installation): * OpenGL and GLX - * GDK 2.0+ or PIL (required for loading images other than PNG and BMP) + * GDK 2.0+ or Pillow (required for loading images other than PNG and BMP) * OpenAL or Pulseaudio (required for playing audio) -**Please note that pyglet v1.4 will likely be the last version to support -Python 2.7**. Future releases of pyglet will be Python 3 only, and will be -targeting OpenGL 3.3+. Previous releases will remain available for download. +**Please note that pyglet v1.5 will likely be the last version to support +legacy OpenGL**. Future releases of pyglet will be targeting OpenGL 3.3+. +Previous releases will remain available for download. Starting with version 1.4, to play compressed audio and video files, you will also need [FFmpeg](https://ffmpeg.org/). @@ -64,7 +62,7 @@ pyglet is installable from PyPI: - pip install --upgrade pyglet --user + pip install --upgrade --user pyglet ## Installation from source @@ -74,7 +72,7 @@ You can also install the latest development version direct from Github using: - pip install --upgrade https://github.com/pyglet/pyglet/archive/master.zip --user + pip install --upgrade --user https://github.com/pyglet/pyglet/archive/master.zip For local development install pyglet in editable mode: @@ -95,7 +93,7 @@ are going to work with, also read the associated docs. If you don't understand the code with the help of the docs, it is a sign that the docs should be improved. -If you want to contribute to Pyglet, we suggest the following: +If you want to contribute to pyglet, we suggest the following: * Fork the [official repository](https://github.com/pyglet/pyglet/fork). * Apply your changes to your fork. diff -Nru pyglet-1.4.10/RELEASE_NOTES pyglet-1.5.14/RELEASE_NOTES --- pyglet-1.4.10/RELEASE_NOTES 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/RELEASE_NOTES 2020-12-31 21:10:58.000000000 +0000 @@ -1,3 +1,210 @@ +pyglet 1.5.14 +Bugfix and feature release + +Bugfixes +-------- +- Fix interlaced mp3 decoding in GStreamer backend. +- Skip functions marked as OBJC_ARM64_UNAVAILABLE for new ARM Macs. + +Features +-------- +- Add a `WaveEncoder` for saving audio Sources to disk. + + +pyglet 1.5.13 +Bugfix and feature release + +Bugfixes +-------- +- Fix crash on looping video with no sound (#322) +- OSX: Replace remaining usage of `find_library` to fix Big Sur issues (#332) +- Windows: Fix default orientation for the XAudio2 listener. + +Features +-------- +- Add new `shape.Triangle` shape. +- Windows: `on_key_press` now dispatches unique events for left/right Shift keys. + + +pyglet 1.5.12 +Bugfix and feature release + +Bugfixes +-------- +- Remove usage of deprecated `parser` module. (#312) + + +pyglet 1.5.11 +Bugfix release + +Bugfixes +-------- +- Added hardcoding of library paths for Big Sur temporary fallback. +- Removed some legacy Python 2 code from the documentation. + + +pyglet 1.5.10 +Bugfix release + +Bugfixes +-------- +- Fix library loading on OSX Big Sur. Currently this only works with the system version of Python. + Non OSX system versions will not yet work, pending upstream fixes: https://bugs.python.org/issue41100 + + +pyglet 1.5.9 +Bugfix and feature release + +Bugfixes +-------- +- Explicitly cast media.synthesis data to bytes to prevent issues on some audio drivers. +- Refactor WIC module to work with new com module. (#298) +- Prevent crash when setting `shapes.Circle.visable`. (#294) +- Remove deprecated `tostring` calls in PIL/PNG decoders to prevent crash on Python 3.9. (#295, #302) + +Improvements +------------ +- Add new Xaudio2 driver. (#288) +- Refactor pyglet's lazy module loading to better support code inspection. +- Added new `TextEntry` widget. + + +pyglet 1.5.8 +Bugfix and feature release + +Bugfixes +-------- + +Improvements +------------ +- Added new experimental `gui` module. Currently this only contains basic widgets. +- Added new `Group.visible` property, to toggle rendering of entire Groups when used in a Batch. +- Added `Sprite.paused` and `Sprite.frame_index` helper properties for controlling Animations. +- Reorganized the examples folder. +- Added new CenteredCamera example. +- Backport pyglet.math from 2.0, for more exposure and testing. +- Consolidate Codec logic into base class to reuse among various modules. + + +pyglet 1.5.7 +Bugfix release + +Bugfixes +-------- +- Fix crash when no audio device is available. +- Prevent `__del__` spam in DirectSound buffers and Wave decoder. +- Explicitly cast warnings.warn message to a str to prevent numba error. + + +pyglet 1.5.6 +Bugfix and feature release + +Bugfixes +-------- +- Fix infinite recursion error on OSX. (#5) +- The on_mouse_scroll event no longer clamps to int, preventing lost motion on trackpads. +- Prevent traceback when DirectSound buffers are garbage collected on exit. +- Fix StaticSource playback with WMF decoder (streaming=False). +- Fix colorspace for WMF video decoding. +- Prevent crash on garbage collection for FFmpeg decoder. + +Improvements +------------ +- New image decoder leveraging Windows Imaging Component (WIC). +- TextureAtlas/Bins now have a `border` argument for leaving n-pixels of blank space around + images when adding. By default the `resource` module leaves a 1-pixel space around images. +- Many small documentation fixes and corrections. + + +pyglet 1.5.5 +Bugfix and feature release + +Bugfixes +-------- +- GStreamer decoder clamped to 16bit sample width, since the default OpenAL backend on Linux does + not support anything higher. + +Improvements +------------ +- shapes module: added `Circle.rotation` property and blending to standalone shape `draw` methods. +- shapes module: added missing `delete` and `__del__` methods. +- The ImageMouseCursor has a new `accelerated` parameter to allow custom mouse points to be drawn + natively on Windows and Linux. + + +pyglet 1.5.4 +Bugfix and minor feature release + +Bugfixes +-------- +- Avoid WMFDecoder crash when mixing pyglet with other tookits. (#174) +- Prevent EventDispatcher subclasses from hiding AttributeError exceptions. + +Improvements +------------ +- Added `pyglet.graphics.shapes` module for creating simple 2D shapes. + + +pyglet 1.5.3 +Bugfix release + +Bugfixes +-------- +- Fix memory leak caused by not freeing stale buffers and textures. + + +pyglet 1.5.2 +Bugfix and minor feature release + +Bugfixes +-------- +- Fix WMF decoder for pre-Windows 8 systems. +- Fix WMF decoder loading from file-like objects for Windows Vista. +- Fix WMF decoder crashing when attempting to seek past file duration. + +Changes +------- +- Allow GStreamer and FFmpeg decoder to load from file-like objects. + + +pyglet 1.5.1 +Bugfix and minor feature release + +Bugfixes +-------- +- Fix issue with changing Label positions by fractional values. +- Avoid performance issue due to unnecessary context switching with single Window programs. +- Fix crash on Player.next_source when playing a video with no audio track. +- Fix crash when disconnecting a Joystick on Windows. +- Prevent media Players from seeking to negative timestamps. +- Fix OpenAL audio driver not dispatching events. + +Changes +-------- +- text.TextLayouts are no longer bound to int positioning. +- Raise exception if attempting to import from legacy Python. +- Add GStreamer decoder for compressed audio on Linux. +- Add Windows Media Foundation decoder for compressed audio on Windows. + +Improvements +------------ +- New "file_drops" Window argument to allow drag and drop of files. This is complimented + by a new `Window.on_file_drop` event, which returns the mouse position and file path. + + +pyglet 1.5.0 +Bugfix and Maintenance release + +Bugfixes +-------- +- Removed global dubug function access in __del__ methods to prevent smooth shutdown. +- Fix regression not allow SourceGroups to be queued on Player objects. (#140) + +Changes +------- +- Support for Python 2 has been dropped. Python 3.5 is now the minimum supported version. + + pyglet 1.4.10 Bugfix release diff -Nru pyglet-1.4.10/setup.cfg pyglet-1.5.14/setup.cfg --- pyglet-1.4.10/setup.cfg 2020-01-17 18:37:44.000000000 +0000 +++ pyglet-1.5.14/setup.cfg 2020-12-31 21:13:06.000000000 +0000 @@ -1,6 +1,3 @@ -[bdist_wheel] -universal = 1 - [egg_info] tag_build = tag_date = 0 diff -Nru pyglet-1.4.10/setup.py pyglet-1.5.14/setup.py --- pyglet-1.4.10/setup.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/setup.py 2020-12-31 21:03:32.000000000 +0000 @@ -1,7 +1,7 @@ #!/usr/bin/env python -import sys from setuptools import setup, find_packages + # Parse version number from pyglet/__init__.py: with open('pyglet/__init__.py') as f: info = {} @@ -10,32 +10,8 @@ exec(line, info) break - -# The source dist comes with batteries included, the wheel can use pip to get the rest -is_wheel = 'bdist_wheel' in sys.argv - -excluded = [] -if is_wheel: - excluded.append('extlibs.future') - - -def exclude_package(pkg): - for exclude in excluded: - if pkg.startswith(exclude): - return True - return False - - -def create_package_list(base_package): - return ([base_package] + - [base_package + '.' + pkg - for pkg - in find_packages(base_package) - if not exclude_package(pkg)]) - setup_info = dict( - # Metadata name='pyglet', version=info['version'], author='Alex Holkner', @@ -61,25 +37,21 @@ 'Operating System :: MacOS :: MacOS X', 'Operating System :: Microsoft :: Windows', 'Operating System :: POSIX :: Linux', - 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', 'Topic :: Games/Entertainment', 'Topic :: Software Development :: Libraries :: Python Modules', ], # Package info - packages=create_package_list('pyglet'), + packages=['pyglet'] + ['pyglet.' + pkg for pkg in find_packages('pyglet')], # Add _ prefix to the names of temporary build dirs options={'build': {'build_base': '_build'}, }, zip_safe=True, ) -if is_wheel: - setup_info['install_requires'] = ['future'] - setup(**setup_info) diff -Nru pyglet-1.4.10/tests/annotations.py pyglet-1.5.14/tests/annotations.py --- pyglet-1.4.10/tests/annotations.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/annotations.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,5 +1,3 @@ -from builtins import object - import os import sys @@ -9,7 +7,7 @@ # Platform identifiers -class Platform(object): +class Platform: """ Predefined lists of identifiers for platforms. For use with :func:`.require_platform` and :func:`.skip_platform`. Combine platforms using +. diff -Nru pyglet-1.4.10/tests/base/data.py pyglet-1.5.14/tests/base/data.py --- pyglet-1.4.10/tests/base/data.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/base/data.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import os import pytest @@ -9,6 +7,7 @@ test_data_path = os.path.abspath(os.path.join(local_dir, '..', 'data')) del local_dir + class PygletTestCase(FutureTestCase): """ Base class for pyglet tests. @@ -23,7 +22,7 @@ return os.path.join(test_data_path, *file_parts) -class TestDataFixture(object): +class TestDataFixture: """Fixture for accessing test data.""" def __init__(self): local_dir = os.path.dirname(__file__) diff -Nru pyglet-1.4.10/tests/base/future_test.py pyglet-1.5.14/tests/base/future_test.py --- pyglet-1.4.10/tests/base/future_test.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/base/future_test.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,30 +1,19 @@ -from builtins import bytes -import sys import unittest -if sys.version_info[:2] < (3, 0): - _py2 = True -else: - _py2 = False - +# TODO: remove this legacy base class class FutureTestCase(unittest.TestCase): """Base class for unittests that adds compatibility for both the Py2 and Py3 version of the unittest module.""" - if _py2: - assertCountEqual = unittest.TestCase.assertItemsEqual + def assertBytesEqual(self, first, second, msg=None): + if isinstance(first, str): + first = first.encode('latin-1') + elif not isinstance(first, bytes): + first = bytes(first) + if isinstance(second, str): + second = second.encode('latin-1') + elif not isinstance(second, bytes): + second = bytes(second) + self.assertEqual(first, second) - if _py2: - assertBytesEqual = unittest.TestCase.assertEqual - else: - def assertBytesEqual(self, first, second, msg=None): - if isinstance(first, str): - first = first.encode('latin-1') - elif not isinstance(first, bytes): - first = bytes(first) - if isinstance(second, str): - second = second.encode('latin-1') - elif not isinstance(second, bytes): - second = bytes(second) - self.assertEqual(first, second) diff -Nru pyglet-1.4.10/tests/base/interactive.py pyglet-1.5.14/tests/base/interactive.py --- pyglet-1.4.10/tests/base/interactive.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/base/interactive.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,13 +1,10 @@ -from __future__ import absolute_import, print_function -from builtins import zip -from builtins import input - -import array import os -import pytest +import array import shutil import warnings +import pytest + import pyglet from pyglet.image import get_buffer_manager @@ -23,7 +20,7 @@ del local_dir, test_dir -class InteractiveFixture(object): +class InteractiveFixture: """Fixture for interactive test cases. Provides interactive prompts and verifying screenshots. """ diff -Nru pyglet-1.4.10/tests/base/performance.py pyglet-1.5.14/tests/base/performance.py --- pyglet-1.4.10/tests/base/performance.py 2019-10-21 21:56:02.000000000 +0000 +++ pyglet-1.5.14/tests/base/performance.py 2020-11-10 10:36:26.000000000 +0000 @@ -5,7 +5,7 @@ import time -class PerformanceTimer(object): +class PerformanceTimer: def __init__(self, max_time): self.max_time = max_time self.start_time = None @@ -17,7 +17,7 @@ assert time.time() - self.start_time < self.max_time -class PerformanceFixture(object): +class PerformanceFixture: timer = PerformanceTimer diff -Nru pyglet-1.4.10/tests/conftest.py pyglet-1.5.14/tests/conftest.py --- pyglet-1.4.10/tests/conftest.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/conftest.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -from __future__ import absolute_import def pytest_addoption(parser): Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/data/media/alert_pcm_16_11025_1ch.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/data/media/alert_pcm_16_11025_1ch.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/data/media/alert_pcm_16_22050_1ch.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/data/media/alert_pcm_16_22050_1ch.wav differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/data/media/alert_pcm_8_22050_1ch.wav and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/data/media/alert_pcm_8_22050_1ch.wav differ diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/builtins/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/builtins/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/builtins/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/builtins/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from __builtin__ import * - # Overwrite any old definitions with the equivalent future.builtins ones: - from future.builtins import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/copyreg/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/copyreg/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/copyreg/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/copyreg/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from copy_reg import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/_dummy_thread/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/_dummy_thread/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/_dummy_thread/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/_dummy_thread/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from dummy_thread import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/html/entities.py pyglet-1.5.14/tests/extlibs/future/py2/html/entities.py --- pyglet-1.4.10/tests/extlibs/future/py2/html/entities.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/html/entities.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from html.entities import * -else: - from future.moves.html.entities import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/html/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/html/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/html/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/html/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from future.moves.html import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/html/parser.py pyglet-1.5.14/tests/extlibs/future/py2/html/parser.py --- pyglet-1.4.10/tests/extlibs/future/py2/html/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/html/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] == 3: - raise ImportError('Cannot import module from python-future source folder') -else: - from future.moves.html.parser import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/http/client.py pyglet-1.5.14/tests/extlibs/future/py2/http/client.py --- pyglet-1.4.10/tests/extlibs/future/py2/http/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/http/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from httplib import * -from httplib import HTTPMessage - -# These constants aren't included in __all__ in httplib.py: - -from httplib import (HTTP_PORT, - HTTPS_PORT, - - _CS_IDLE, - _CS_REQ_STARTED, - _CS_REQ_SENT, - - CONTINUE, - SWITCHING_PROTOCOLS, - PROCESSING, - - OK, - CREATED, - ACCEPTED, - NON_AUTHORITATIVE_INFORMATION, - NO_CONTENT, - RESET_CONTENT, - PARTIAL_CONTENT, - MULTI_STATUS, - IM_USED, - - MULTIPLE_CHOICES, - MOVED_PERMANENTLY, - FOUND, - SEE_OTHER, - NOT_MODIFIED, - USE_PROXY, - TEMPORARY_REDIRECT, - - BAD_REQUEST, - UNAUTHORIZED, - PAYMENT_REQUIRED, - FORBIDDEN, - NOT_FOUND, - METHOD_NOT_ALLOWED, - NOT_ACCEPTABLE, - PROXY_AUTHENTICATION_REQUIRED, - REQUEST_TIMEOUT, - CONFLICT, - GONE, - LENGTH_REQUIRED, - PRECONDITION_FAILED, - REQUEST_ENTITY_TOO_LARGE, - REQUEST_URI_TOO_LONG, - UNSUPPORTED_MEDIA_TYPE, - REQUESTED_RANGE_NOT_SATISFIABLE, - EXPECTATION_FAILED, - UNPROCESSABLE_ENTITY, - LOCKED, - FAILED_DEPENDENCY, - UPGRADE_REQUIRED, - - INTERNAL_SERVER_ERROR, - NOT_IMPLEMENTED, - BAD_GATEWAY, - SERVICE_UNAVAILABLE, - GATEWAY_TIMEOUT, - HTTP_VERSION_NOT_SUPPORTED, - INSUFFICIENT_STORAGE, - NOT_EXTENDED, - - MAXAMOUNT, - ) - -# These are not available on Python 2.6.x: -try: - from httplib import LineTooLong, LineAndFileWrapper -except ImportError: - pass - -# These may not be available on all versions of Python 2.6.x or 2.7.x -try: - from httplib import ( - _MAXLINE, - _MAXHEADERS, - _is_legal_header_name, - _is_illegal_header_value, - _METHODS_EXPECTING_BODY - ) -except ImportError: - pass diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/http/cookiejar.py pyglet-1.5.14/tests/extlibs/future/py2/http/cookiejar.py --- pyglet-1.4.10/tests/extlibs/future/py2/http/cookiejar.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/http/cookiejar.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from cookielib import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/http/cookies.py pyglet-1.5.14/tests/extlibs/future/py2/http/cookies.py --- pyglet-1.4.10/tests/extlibs/future/py2/http/cookies.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/http/cookies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from Cookie import * -from Cookie import Morsel # left out of __all__ on Py2.7! diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/http/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/http/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/http/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/http/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - pass -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/http/server.py pyglet-1.5.14/tests/extlibs/future/py2/http/server.py --- pyglet-1.4.10/tests/extlibs/future/py2/http/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/http/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 - -from BaseHTTPServer import * -from CGIHTTPServer import * -from SimpleHTTPServer import * -try: - from CGIHTTPServer import _url_collapse_path # needed for a test -except ImportError: - try: - # Python 2.7.0 to 2.7.3 - from CGIHTTPServer import ( - _url_collapse_path_split as _url_collapse_path) - except ImportError: - # Doesn't exist on Python 2.6.x. Ignore it. - pass diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/_markupbase/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/_markupbase/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/_markupbase/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/_markupbase/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from markupbase import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/queue/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/queue/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/queue/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/queue/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from Queue import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/reprlib/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/reprlib/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/reprlib/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/reprlib/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from repr import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/socketserver/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/socketserver/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/socketserver/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/socketserver/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from SocketServer import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/_thread/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/_thread/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/_thread/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/_thread/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from thread import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/colorchooser.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/colorchooser.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/colorchooser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/colorchooser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.colorchooser import * -else: - try: - from tkColorChooser import * - except ImportError: - raise ImportError('The tkColorChooser module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/commondialog.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/commondialog.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/commondialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/commondialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.commondialog import * -else: - try: - from tkCommonDialog import * - except ImportError: - raise ImportError('The tkCommonDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/constants.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/constants.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/constants.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/constants.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.constants import * -else: - try: - from Tkconstants import * - except ImportError: - raise ImportError('The Tkconstants module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/dialog.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/dialog.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/dialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/dialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dialog import * -else: - try: - from Dialog import * - except ImportError: - raise ImportError('The Dialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/dnd.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/dnd.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/dnd.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/dnd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dnd import * -else: - try: - from Tkdnd import * - except ImportError: - raise ImportError('The Tkdnd module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/filedialog.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/filedialog.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/filedialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/filedialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.filedialog import * -else: - try: - from FileDialog import * - except ImportError: - raise ImportError('The FileDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - try: - from tkFileDialog import * - except ImportError: - raise ImportError('The tkFileDialog module is missing. Does your Py2 ' - 'installation include tkinter?') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/font.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/font.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/font.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/font.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.font import * -else: - try: - from tkFont import * - except ImportError: - raise ImportError('The tkFont module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - from Tkinter import * - from Tkinter import (_cnfmerge, _default_root, _flatten, _join, _setit, - _splitdict, _stringify, _support_default_root, _test, - _tkinter) -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/messagebox.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/messagebox.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/messagebox.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/messagebox.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.messagebox import * -else: - try: - from tkMessageBox import * - except ImportError: - raise ImportError('The tkMessageBox module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/scrolledtext.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/scrolledtext.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/scrolledtext.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/scrolledtext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.scrolledtext import * -else: - try: - from ScrolledText import * - except ImportError: - raise ImportError('The ScrolledText module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/simpledialog.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/simpledialog.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/simpledialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/simpledialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.simpledialog import * -else: - try: - from SimpleDialog import * - except ImportError: - raise ImportError('The SimpleDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/tix.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/tix.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/tix.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/tix.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.tix import * -else: - try: - from Tix import * - except ImportError: - raise ImportError('The Tix module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/tkinter/ttk.py pyglet-1.5.14/tests/extlibs/future/py2/tkinter/ttk.py --- pyglet-1.4.10/tests/extlibs/future/py2/tkinter/ttk.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/tkinter/ttk.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.ttk import * -else: - try: - from ttk import * - except ImportError: - raise ImportError('The ttk module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/winreg/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/winreg/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/winreg/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/winreg/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -import sys -__future_module__ = True - -if sys.version_info[0] < 3: - from _winreg import * -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/xmlrpc/client.py pyglet-1.5.14/tests/extlibs/future/py2/xmlrpc/client.py --- pyglet-1.4.10/tests/extlibs/future/py2/xmlrpc/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/xmlrpc/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 -from xmlrpclib import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/xmlrpc/__init__.py pyglet-1.5.14/tests/extlibs/future/py2/xmlrpc/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2/xmlrpc/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/xmlrpc/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -import sys - -if sys.version_info[0] < 3: - pass -else: - raise ImportError('This package should not be accessible on Python 3. ' - 'Either you are trying to run from the python-future src folder ' - 'or your installation of python-future is corrupted.') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2/xmlrpc/server.py pyglet-1.5.14/tests/extlibs/future/py2/xmlrpc/server.py --- pyglet-1.4.10/tests/extlibs/future/py2/xmlrpc/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2/xmlrpc/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -from __future__ import absolute_import -import sys - -assert sys.version_info[0] < 3 -from xmlrpclib import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/datetime.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/datetime.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/datetime.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/datetime.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2152 +0,0 @@ -"""Concrete date/time and related types. - -See http://www.iana.org/time-zones/repository/tz-link.html for -time zone and DST data sources. -""" -from __future__ import division -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import absolute_import -from future.builtins import str -from future.builtins import bytes -from future.builtins import map -from future.builtins import round -from future.builtins import int -from future.builtins import object -from future.utils import native_str, PY2 - -import time as _time -import math as _math - -def _cmp(x, y): - return 0 if x == y else 1 if x > y else -1 - -MINYEAR = 1 -MAXYEAR = 9999 -_MAXORDINAL = 3652059 # date.max.toordinal() - -# Utility functions, adapted from Python's Demo/classes/Dates.py, which -# also assumes the current Gregorian calendar indefinitely extended in -# both directions. Difference: Dates.py calls January 1 of year 0 day -# number 1. The code here calls January 1 of year 1 day number 1. This is -# to match the definition of the "proleptic Gregorian" calendar in Dershowitz -# and Reingold's "Calendrical Calculations", where it's the base calendar -# for all computations. See the book for algorithms for converting between -# proleptic Gregorian ordinals and many other calendar systems. - -_DAYS_IN_MONTH = [None, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] - -_DAYS_BEFORE_MONTH = [None] -dbm = 0 -for dim in _DAYS_IN_MONTH[1:]: - _DAYS_BEFORE_MONTH.append(dbm) - dbm += dim -del dbm, dim - -def _is_leap(year): - "year -> 1 if leap year, else 0." - return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0) - -def _days_before_year(year): - "year -> number of days before January 1st of year." - y = year - 1 - return y*365 + y//4 - y//100 + y//400 - -def _days_in_month(year, month): - "year, month -> number of days in that month in that year." - assert 1 <= month <= 12, month - if month == 2 and _is_leap(year): - return 29 - return _DAYS_IN_MONTH[month] - -def _days_before_month(year, month): - "year, month -> number of days in year preceding first day of month." - assert 1 <= month <= 12, 'month must be in 1..12' - return _DAYS_BEFORE_MONTH[month] + (month > 2 and _is_leap(year)) - -def _ymd2ord(year, month, day): - "year, month, day -> ordinal, considering 01-Jan-0001 as day 1." - assert 1 <= month <= 12, 'month must be in 1..12' - dim = _days_in_month(year, month) - assert 1 <= day <= dim, ('day must be in 1..%d' % dim) - return (_days_before_year(year) + - _days_before_month(year, month) + - day) - -_DI400Y = _days_before_year(401) # number of days in 400 years -_DI100Y = _days_before_year(101) # " " " " 100 " -_DI4Y = _days_before_year(5) # " " " " 4 " - -# A 4-year cycle has an extra leap day over what we'd get from pasting -# together 4 single years. -assert _DI4Y == 4 * 365 + 1 - -# Similarly, a 400-year cycle has an extra leap day over what we'd get from -# pasting together 4 100-year cycles. -assert _DI400Y == 4 * _DI100Y + 1 - -# OTOH, a 100-year cycle has one fewer leap day than we'd get from -# pasting together 25 4-year cycles. -assert _DI100Y == 25 * _DI4Y - 1 - -def _ord2ymd(n): - "ordinal -> (year, month, day), considering 01-Jan-0001 as day 1." - - # n is a 1-based index, starting at 1-Jan-1. The pattern of leap years - # repeats exactly every 400 years. The basic strategy is to find the - # closest 400-year boundary at or before n, then work with the offset - # from that boundary to n. Life is much clearer if we subtract 1 from - # n first -- then the values of n at 400-year boundaries are exactly - # those divisible by _DI400Y: - # - # D M Y n n-1 - # -- --- ---- ---------- ---------------- - # 31 Dec -400 -_DI400Y -_DI400Y -1 - # 1 Jan -399 -_DI400Y +1 -_DI400Y 400-year boundary - # ... - # 30 Dec 000 -1 -2 - # 31 Dec 000 0 -1 - # 1 Jan 001 1 0 400-year boundary - # 2 Jan 001 2 1 - # 3 Jan 001 3 2 - # ... - # 31 Dec 400 _DI400Y _DI400Y -1 - # 1 Jan 401 _DI400Y +1 _DI400Y 400-year boundary - n -= 1 - n400, n = divmod(n, _DI400Y) - year = n400 * 400 + 1 # ..., -399, 1, 401, ... - - # Now n is the (non-negative) offset, in days, from January 1 of year, to - # the desired date. Now compute how many 100-year cycles precede n. - # Note that it's possible for n100 to equal 4! In that case 4 full - # 100-year cycles precede the desired day, which implies the desired - # day is December 31 at the end of a 400-year cycle. - n100, n = divmod(n, _DI100Y) - - # Now compute how many 4-year cycles precede it. - n4, n = divmod(n, _DI4Y) - - # And now how many single years. Again n1 can be 4, and again meaning - # that the desired day is December 31 at the end of the 4-year cycle. - n1, n = divmod(n, 365) - - year += n100 * 100 + n4 * 4 + n1 - if n1 == 4 or n100 == 4: - assert n == 0 - return year-1, 12, 31 - - # Now the year is correct, and n is the offset from January 1. We find - # the month via an estimate that's either exact or one too large. - leapyear = n1 == 3 and (n4 != 24 or n100 == 3) - assert leapyear == _is_leap(year) - month = (n + 50) >> 5 - preceding = _DAYS_BEFORE_MONTH[month] + (month > 2 and leapyear) - if preceding > n: # estimate is too large - month -= 1 - preceding -= _DAYS_IN_MONTH[month] + (month == 2 and leapyear) - n -= preceding - assert 0 <= n < _days_in_month(year, month) - - # Now the year and month are correct, and n is the offset from the - # start of that month: we're done! - return year, month, n+1 - -# Month and day names. For localized versions, see the calendar module. -_MONTHNAMES = [None, "Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -_DAYNAMES = [None, "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] - - -def _build_struct_time(y, m, d, hh, mm, ss, dstflag): - wday = (_ymd2ord(y, m, d) + 6) % 7 - dnum = _days_before_month(y, m) + d - return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag)) - -def _format_time(hh, mm, ss, us): - # Skip trailing microseconds when us==0. - result = "%02d:%02d:%02d" % (hh, mm, ss) - if us: - result += ".%06d" % us - return result - -# Correctly substitute for %z and %Z escapes in strftime formats. -def _wrap_strftime(object, format, timetuple): - # Don't call utcoffset() or tzname() unless actually needed. - freplace = None # the string to use for %f - zreplace = None # the string to use for %z - Zreplace = None # the string to use for %Z - - # Scan format for %z and %Z escapes, replacing as needed. - newformat = [] - push = newformat.append - i, n = 0, len(format) - while i < n: - ch = format[i] - i += 1 - if ch == '%': - if i < n: - ch = format[i] - i += 1 - if ch == 'f': - if freplace is None: - freplace = '%06d' % getattr(object, - 'microsecond', 0) - newformat.append(freplace) - elif ch == 'z': - if zreplace is None: - zreplace = "" - if hasattr(object, "utcoffset"): - offset = object.utcoffset() - if offset is not None: - sign = '+' - if offset.days < 0: - offset = -offset - sign = '-' - h, m = divmod(offset, timedelta(hours=1)) - assert not m % timedelta(minutes=1), "whole minute" - m //= timedelta(minutes=1) - zreplace = '%c%02d%02d' % (sign, h, m) - assert '%' not in zreplace - newformat.append(zreplace) - elif ch == 'Z': - if Zreplace is None: - Zreplace = "" - if hasattr(object, "tzname"): - s = object.tzname() - if s is not None: - # strftime is going to have at this: escape % - Zreplace = s.replace('%', '%%') - newformat.append(Zreplace) - else: - push('%') - push(ch) - else: - push('%') - else: - push(ch) - newformat = "".join(newformat) - return _time.strftime(newformat, timetuple) - -def _call_tzinfo_method(tzinfo, methname, tzinfoarg): - if tzinfo is None: - return None - return getattr(tzinfo, methname)(tzinfoarg) - -# Just raise TypeError if the arg isn't None or a string. -def _check_tzname(name): - if name is not None and not isinstance(name, str): - raise TypeError("tzinfo.tzname() must return None or string, " - "not '%s'" % type(name)) - -# name is the offset-producing method, "utcoffset" or "dst". -# offset is what it returned. -# If offset isn't None or timedelta, raises TypeError. -# If offset is None, returns None. -# Else offset is checked for being in range, and a whole # of minutes. -# If it is, its integer value is returned. Else ValueError is raised. -def _check_utc_offset(name, offset): - assert name in ("utcoffset", "dst") - if offset is None: - return - if not isinstance(offset, timedelta): - raise TypeError("tzinfo.%s() must return None " - "or timedelta, not '%s'" % (name, type(offset))) - if offset % timedelta(minutes=1) or offset.microseconds: - raise ValueError("tzinfo.%s() must return a whole number " - "of minutes, got %s" % (name, offset)) - if not -timedelta(1) < offset < timedelta(1): - raise ValueError("%s()=%s, must be must be strictly between" - " -timedelta(hours=24) and timedelta(hours=24)" - % (name, offset)) - -def _check_date_fields(year, month, day): - if not isinstance(year, int): - raise TypeError('int expected') - if not MINYEAR <= year <= MAXYEAR: - raise ValueError('year must be in %d..%d' % (MINYEAR, MAXYEAR), year) - if not 1 <= month <= 12: - raise ValueError('month must be in 1..12', month) - dim = _days_in_month(year, month) - if not 1 <= day <= dim: - raise ValueError('day must be in 1..%d' % dim, day) - -def _check_time_fields(hour, minute, second, microsecond): - if not isinstance(hour, int): - raise TypeError('int expected') - if not 0 <= hour <= 23: - raise ValueError('hour must be in 0..23', hour) - if not 0 <= minute <= 59: - raise ValueError('minute must be in 0..59', minute) - if not 0 <= second <= 59: - raise ValueError('second must be in 0..59', second) - if not 0 <= microsecond <= 999999: - raise ValueError('microsecond must be in 0..999999', microsecond) - -def _check_tzinfo_arg(tz): - if tz is not None and not isinstance(tz, tzinfo): - raise TypeError("tzinfo argument must be None or of a tzinfo subclass") - -def _cmperror(x, y): - raise TypeError("can't compare '%s' to '%s'" % ( - type(x).__name__, type(y).__name__)) - -class timedelta(object): - """Represent the difference between two datetime objects. - - Supported operators: - - - add, subtract timedelta - - unary plus, minus, abs - - compare to timedelta - - multiply, divide by int - - In addition, datetime supports subtraction of two datetime objects - returning a timedelta, and addition or subtraction of a datetime - and a timedelta giving a datetime. - - Representation: (days, seconds, microseconds). Why? Because I - felt like it. - """ - __slots__ = '_days', '_seconds', '_microseconds' - - def __new__(cls, days=0, seconds=0, microseconds=0, - milliseconds=0, minutes=0, hours=0, weeks=0): - # Doing this efficiently and accurately in C is going to be difficult - # and error-prone, due to ubiquitous overflow possibilities, and that - # C double doesn't have enough bits of precision to represent - # microseconds over 10K years faithfully. The code here tries to make - # explicit where go-fast assumptions can be relied on, in order to - # guide the C implementation; it's way more convoluted than speed- - # ignoring auto-overflow-to-long idiomatic Python could be. - - # XXX Check that all inputs are ints or floats. - - # Final values, all integer. - # s and us fit in 32-bit signed ints; d isn't bounded. - d = s = us = 0 - - # Normalize everything to days, seconds, microseconds. - days += weeks*7 - seconds += minutes*60 + hours*3600 - microseconds += milliseconds*1000 - - # Get rid of all fractions, and normalize s and us. - # Take a deep breath . - if isinstance(days, float): - dayfrac, days = _math.modf(days) - daysecondsfrac, daysecondswhole = _math.modf(dayfrac * (24.*3600.)) - assert daysecondswhole == int(daysecondswhole) # can't overflow - s = int(daysecondswhole) - assert days == int(days) - d = int(days) - else: - daysecondsfrac = 0.0 - d = days - assert isinstance(daysecondsfrac, float) - assert abs(daysecondsfrac) <= 1.0 - assert isinstance(d, int) - assert abs(s) <= 24 * 3600 - # days isn't referenced again before redefinition - - if isinstance(seconds, float): - secondsfrac, seconds = _math.modf(seconds) - assert seconds == int(seconds) - seconds = int(seconds) - secondsfrac += daysecondsfrac - assert abs(secondsfrac) <= 2.0 - else: - secondsfrac = daysecondsfrac - # daysecondsfrac isn't referenced again - assert isinstance(secondsfrac, float) - assert abs(secondsfrac) <= 2.0 - - assert isinstance(seconds, int) - days, seconds = divmod(seconds, 24*3600) - d += days - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 2 * 24 * 3600 - # seconds isn't referenced again before redefinition - - usdouble = secondsfrac * 1e6 - assert abs(usdouble) < 2.1e6 # exact value not critical - # secondsfrac isn't referenced again - - if isinstance(microseconds, float): - microseconds += usdouble - microseconds = round(microseconds, 0) - seconds, microseconds = divmod(microseconds, 1e6) - assert microseconds == int(microseconds) - assert seconds == int(seconds) - days, seconds = divmod(seconds, 24.*3600.) - assert days == int(days) - assert seconds == int(seconds) - d += int(days) - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 3 * 24 * 3600 - else: - seconds, microseconds = divmod(microseconds, 1000000) - days, seconds = divmod(seconds, 24*3600) - d += days - s += int(seconds) # can't overflow - assert isinstance(s, int) - assert abs(s) <= 3 * 24 * 3600 - microseconds = float(microseconds) - microseconds += usdouble - microseconds = round(microseconds, 0) - assert abs(s) <= 3 * 24 * 3600 - assert abs(microseconds) < 3.1e6 - - # Just a little bit of carrying possible for microseconds and seconds. - assert isinstance(microseconds, float) - assert int(microseconds) == microseconds - us = int(microseconds) - seconds, us = divmod(us, 1000000) - s += seconds # cant't overflow - assert isinstance(s, int) - days, s = divmod(s, 24*3600) - d += days - - assert isinstance(d, int) - assert isinstance(s, int) and 0 <= s < 24*3600 - assert isinstance(us, int) and 0 <= us < 1000000 - - self = object.__new__(cls) - - self._days = d - self._seconds = s - self._microseconds = us - if abs(d) > 999999999: - raise OverflowError("timedelta # of days is too large: %d" % d) - - return self - - def __repr__(self): - if self._microseconds: - return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__, - self._days, - self._seconds, - self._microseconds) - if self._seconds: - return "%s(%d, %d)" % ('datetime.' + self.__class__.__name__, - self._days, - self._seconds) - return "%s(%d)" % ('datetime.' + self.__class__.__name__, self._days) - - def __str__(self): - mm, ss = divmod(self._seconds, 60) - hh, mm = divmod(mm, 60) - s = "%d:%02d:%02d" % (hh, mm, ss) - if self._days: - def plural(n): - return n, abs(n) != 1 and "s" or "" - s = ("%d day%s, " % plural(self._days)) + s - if self._microseconds: - s = s + ".%06d" % self._microseconds - return s - - def total_seconds(self): - """Total seconds in the duration.""" - return ((self.days * 86400 + self.seconds)*10**6 + - self.microseconds) / 10**6 - - # Read-only field accessors - @property - def days(self): - """days""" - return self._days - - @property - def seconds(self): - """seconds""" - return self._seconds - - @property - def microseconds(self): - """microseconds""" - return self._microseconds - - def __add__(self, other): - if isinstance(other, timedelta): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(self._days + other._days, - self._seconds + other._seconds, - self._microseconds + other._microseconds) - return NotImplemented - - __radd__ = __add__ - - def __sub__(self, other): - if isinstance(other, timedelta): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(self._days - other._days, - self._seconds - other._seconds, - self._microseconds - other._microseconds) - return NotImplemented - - def __rsub__(self, other): - if isinstance(other, timedelta): - return -self + other - return NotImplemented - - def __neg__(self): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(-self._days, - -self._seconds, - -self._microseconds) - - def __pos__(self): - return self - - def __abs__(self): - if self._days < 0: - return -self - else: - return self - - def __mul__(self, other): - if isinstance(other, int): - # for CPython compatibility, we cannot use - # our __class__ here, but need a real timedelta - return timedelta(self._days * other, - self._seconds * other, - self._microseconds * other) - if isinstance(other, float): - a, b = other.as_integer_ratio() - return self * a / b - return NotImplemented - - __rmul__ = __mul__ - - def _to_microseconds(self): - return ((self._days * (24*3600) + self._seconds) * 1000000 + - self._microseconds) - - def __floordiv__(self, other): - if not isinstance(other, (int, timedelta)): - return NotImplemented - usec = self._to_microseconds() - if isinstance(other, timedelta): - return usec // other._to_microseconds() - if isinstance(other, int): - return timedelta(0, 0, usec // other) - - def __truediv__(self, other): - if not isinstance(other, (int, float, timedelta)): - return NotImplemented - usec = self._to_microseconds() - if isinstance(other, timedelta): - return usec / other._to_microseconds() - if isinstance(other, int): - return timedelta(0, 0, usec / other) - if isinstance(other, float): - a, b = other.as_integer_ratio() - return timedelta(0, 0, b * usec / a) - - def __mod__(self, other): - if isinstance(other, timedelta): - r = self._to_microseconds() % other._to_microseconds() - return timedelta(0, 0, r) - return NotImplemented - - def __divmod__(self, other): - if isinstance(other, timedelta): - q, r = divmod(self._to_microseconds(), - other._to_microseconds()) - return q, timedelta(0, 0, r) - return NotImplemented - - # Comparisons of timedelta objects with other. - - def __eq__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) == 0 - else: - return False - - def __ne__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) != 0 - else: - return True - - def __le__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) <= 0 - else: - _cmperror(self, other) - - def __lt__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) < 0 - else: - _cmperror(self, other) - - def __ge__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) >= 0 - else: - _cmperror(self, other) - - def __gt__(self, other): - if isinstance(other, timedelta): - return self._cmp(other) > 0 - else: - _cmperror(self, other) - - def _cmp(self, other): - assert isinstance(other, timedelta) - return _cmp(self._getstate(), other._getstate()) - - def __hash__(self): - return hash(self._getstate()) - - def __bool__(self): - return (self._days != 0 or - self._seconds != 0 or - self._microseconds != 0) - - # Pickle support. - - def _getstate(self): - return (self._days, self._seconds, self._microseconds) - - def __reduce__(self): - return (self.__class__, self._getstate()) - -timedelta.min = timedelta(-999999999) -timedelta.max = timedelta(days=999999999, hours=23, minutes=59, seconds=59, - microseconds=999999) -timedelta.resolution = timedelta(microseconds=1) - -class date(object): - """Concrete date type. - - Constructors: - - __new__() - fromtimestamp() - today() - fromordinal() - - Operators: - - __repr__, __str__ - __cmp__, __hash__ - __add__, __radd__, __sub__ (add/radd only with timedelta arg) - - Methods: - - timetuple() - toordinal() - weekday() - isoweekday(), isocalendar(), isoformat() - ctime() - strftime() - - Properties (readonly): - year, month, day - """ - __slots__ = '_year', '_month', '_day' - - def __new__(cls, year, month=None, day=None): - """Constructor. - - Arguments: - - year, month, day (required, base 1) - """ - if (isinstance(year, bytes) and len(year) == 4 and - 1 <= year[2] <= 12 and month is None): # Month is sane - # Pickle support - self = object.__new__(cls) - self.__setstate(year) - return self - _check_date_fields(year, month, day) - self = object.__new__(cls) - self._year = year - self._month = month - self._day = day - return self - - # Additional constructors - - @classmethod - def fromtimestamp(cls, t): - "Construct a date from a POSIX timestamp (like time.time())." - y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t) - return cls(y, m, d) - - @classmethod - def today(cls): - "Construct a date from time.time()." - t = _time.time() - return cls.fromtimestamp(t) - - @classmethod - def fromordinal(cls, n): - """Contruct a date from a proleptic Gregorian ordinal. - - January 1 of year 1 is day 1. Only the year, month and day are - non-zero in the result. - """ - y, m, d = _ord2ymd(n) - return cls(y, m, d) - - # Conversions to string - - def __repr__(self): - """Convert to formal string, for repr(). - - >>> dt = datetime(2010, 1, 1) - >>> repr(dt) - 'datetime.datetime(2010, 1, 1, 0, 0)' - - >>> dt = datetime(2010, 1, 1, tzinfo=timezone.utc) - >>> repr(dt) - 'datetime.datetime(2010, 1, 1, 0, 0, tzinfo=datetime.timezone.utc)' - """ - return "%s(%d, %d, %d)" % ('datetime.' + self.__class__.__name__, - self._year, - self._month, - self._day) - # XXX These shouldn't depend on time.localtime(), because that - # clips the usable dates to [1970 .. 2038). At least ctime() is - # easily done without using strftime() -- that's better too because - # strftime("%c", ...) is locale specific. - - - def ctime(self): - "Return ctime() style string." - weekday = self.toordinal() % 7 or 7 - return "%s %s %2d 00:00:00 %04d" % ( - _DAYNAMES[weekday], - _MONTHNAMES[self._month], - self._day, self._year) - - def strftime(self, fmt): - "Format using strftime()." - return _wrap_strftime(self, fmt, self.timetuple()) - - def __format__(self, fmt): - if len(fmt) != 0: - return self.strftime(fmt) - return str(self) - - def isoformat(self): - """Return the date formatted according to ISO. - - This is 'YYYY-MM-DD'. - - References: - - http://www.w3.org/TR/NOTE-datetime - - http://www.cl.cam.ac.uk/~mgk25/iso-time.html - """ - return "%04d-%02d-%02d" % (self._year, self._month, self._day) - - __str__ = isoformat - - # Read-only field accessors - @property - def year(self): - """year (1-9999)""" - return self._year - - @property - def month(self): - """month (1-12)""" - return self._month - - @property - def day(self): - """day (1-31)""" - return self._day - - # Standard conversions, __cmp__, __hash__ (and helpers) - - def timetuple(self): - "Return local time tuple compatible with time.localtime()." - return _build_struct_time(self._year, self._month, self._day, - 0, 0, 0, -1) - - def toordinal(self): - """Return proleptic Gregorian ordinal for the year, month and day. - - January 1 of year 1 is day 1. Only the year, month and day values - contribute to the result. - """ - return _ymd2ord(self._year, self._month, self._day) - - def replace(self, year=None, month=None, day=None): - """Return a new date with new values for the specified fields.""" - if year is None: - year = self._year - if month is None: - month = self._month - if day is None: - day = self._day - _check_date_fields(year, month, day) - return date(year, month, day) - - # Comparisons of date objects with other. - - def __eq__(self, other): - if isinstance(other, date): - return self._cmp(other) == 0 - return NotImplemented - - def __ne__(self, other): - if isinstance(other, date): - return self._cmp(other) != 0 - return NotImplemented - - def __le__(self, other): - if isinstance(other, date): - return self._cmp(other) <= 0 - return NotImplemented - - def __lt__(self, other): - if isinstance(other, date): - return self._cmp(other) < 0 - return NotImplemented - - def __ge__(self, other): - if isinstance(other, date): - return self._cmp(other) >= 0 - return NotImplemented - - def __gt__(self, other): - if isinstance(other, date): - return self._cmp(other) > 0 - return NotImplemented - - def _cmp(self, other): - assert isinstance(other, date) - y, m, d = self._year, self._month, self._day - y2, m2, d2 = other._year, other._month, other._day - return _cmp((y, m, d), (y2, m2, d2)) - - def __hash__(self): - "Hash." - return hash(self._getstate()) - - # Computations - - def __add__(self, other): - "Add a date to a timedelta." - if isinstance(other, timedelta): - o = self.toordinal() + other.days - if 0 < o <= _MAXORDINAL: - return date.fromordinal(o) - raise OverflowError("result out of range") - return NotImplemented - - __radd__ = __add__ - - def __sub__(self, other): - """Subtract two dates, or a date and a timedelta.""" - if isinstance(other, timedelta): - return self + timedelta(-other.days) - if isinstance(other, date): - days1 = self.toordinal() - days2 = other.toordinal() - return timedelta(days1 - days2) - return NotImplemented - - def weekday(self): - "Return day of the week, where Monday == 0 ... Sunday == 6." - return (self.toordinal() + 6) % 7 - - # Day-of-the-week and week-of-the-year, according to ISO - - def isoweekday(self): - "Return day of the week, where Monday == 1 ... Sunday == 7." - # 1-Jan-0001 is a Monday - return self.toordinal() % 7 or 7 - - def isocalendar(self): - """Return a 3-tuple containing ISO year, week number, and weekday. - - The first ISO week of the year is the (Mon-Sun) week - containing the year's first Thursday; everything else derives - from that. - - The first week is 1; Monday is 1 ... Sunday is 7. - - ISO calendar algorithm taken from - http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm - """ - year = self._year - week1monday = _isoweek1monday(year) - today = _ymd2ord(self._year, self._month, self._day) - # Internally, week and day have origin 0 - week, day = divmod(today - week1monday, 7) - if week < 0: - year -= 1 - week1monday = _isoweek1monday(year) - week, day = divmod(today - week1monday, 7) - elif week >= 52: - if today >= _isoweek1monday(year+1): - year += 1 - week = 0 - return year, week+1, day+1 - - # Pickle support. - - def _getstate(self): - yhi, ylo = divmod(self._year, 256) - return bytes([yhi, ylo, self._month, self._day]), - - def __setstate(self, string): - if len(string) != 4 or not (1 <= string[2] <= 12): - raise TypeError("not enough arguments") - yhi, ylo, self._month, self._day = string - self._year = yhi * 256 + ylo - - def __reduce__(self): - return (self.__class__, self._getstate()) - -_date_class = date # so functions w/ args named "date" can get at the class - -date.min = date(1, 1, 1) -date.max = date(9999, 12, 31) -date.resolution = timedelta(days=1) - -class tzinfo(object): - """Abstract base class for time zone info classes. - - Subclasses must override the name(), utcoffset() and dst() methods. - """ - __slots__ = () - def tzname(self, dt): - "datetime -> string name of time zone." - raise NotImplementedError("tzinfo subclass must override tzname()") - - def utcoffset(self, dt): - "datetime -> minutes east of UTC (negative for west of UTC)" - raise NotImplementedError("tzinfo subclass must override utcoffset()") - - def dst(self, dt): - """datetime -> DST offset in minutes east of UTC. - - Return 0 if DST not in effect. utcoffset() must include the DST - offset. - """ - raise NotImplementedError("tzinfo subclass must override dst()") - - def fromutc(self, dt): - "datetime in UTC -> datetime in local time." - - if not isinstance(dt, datetime): - raise TypeError("fromutc() requires a datetime argument") - if dt.tzinfo is not self: - raise ValueError("dt.tzinfo is not self") - - dtoff = dt.utcoffset() - if dtoff is None: - raise ValueError("fromutc() requires a non-None utcoffset() " - "result") - - # See the long comment block at the end of this file for an - # explanation of this algorithm. - dtdst = dt.dst() - if dtdst is None: - raise ValueError("fromutc() requires a non-None dst() result") - delta = dtoff - dtdst - if delta: - dt += delta - dtdst = dt.dst() - if dtdst is None: - raise ValueError("fromutc(): dt.dst gave inconsistent " - "results; cannot convert") - return dt + dtdst - - # Pickle support. - - def __reduce__(self): - getinitargs = getattr(self, "__getinitargs__", None) - if getinitargs: - args = getinitargs() - else: - args = () - getstate = getattr(self, "__getstate__", None) - if getstate: - state = getstate() - else: - state = getattr(self, "__dict__", None) or None - if state is None: - return (self.__class__, args) - else: - return (self.__class__, args, state) - -_tzinfo_class = tzinfo - -class time(object): - """Time with time zone. - - Constructors: - - __new__() - - Operators: - - __repr__, __str__ - __cmp__, __hash__ - - Methods: - - strftime() - isoformat() - utcoffset() - tzname() - dst() - - Properties (readonly): - hour, minute, second, microsecond, tzinfo - """ - - def __new__(cls, hour=0, minute=0, second=0, microsecond=0, tzinfo=None): - """Constructor. - - Arguments: - - hour, minute (required) - second, microsecond (default to zero) - tzinfo (default to None) - """ - self = object.__new__(cls) - if isinstance(hour, bytes) and len(hour) == 6: - # Pickle support - self.__setstate(hour, minute or None) - return self - _check_tzinfo_arg(tzinfo) - _check_time_fields(hour, minute, second, microsecond) - self._hour = hour - self._minute = minute - self._second = second - self._microsecond = microsecond - self._tzinfo = tzinfo - return self - - # Read-only field accessors - @property - def hour(self): - """hour (0-23)""" - return self._hour - - @property - def minute(self): - """minute (0-59)""" - return self._minute - - @property - def second(self): - """second (0-59)""" - return self._second - - @property - def microsecond(self): - """microsecond (0-999999)""" - return self._microsecond - - @property - def tzinfo(self): - """timezone info object""" - return self._tzinfo - - # Standard conversions, __hash__ (and helpers) - - # Comparisons of time objects with other. - - def __eq__(self, other): - if isinstance(other, time): - return self._cmp(other, allow_mixed=True) == 0 - else: - return False - - def __ne__(self, other): - if isinstance(other, time): - return self._cmp(other, allow_mixed=True) != 0 - else: - return True - - def __le__(self, other): - if isinstance(other, time): - return self._cmp(other) <= 0 - else: - _cmperror(self, other) - - def __lt__(self, other): - if isinstance(other, time): - return self._cmp(other) < 0 - else: - _cmperror(self, other) - - def __ge__(self, other): - if isinstance(other, time): - return self._cmp(other) >= 0 - else: - _cmperror(self, other) - - def __gt__(self, other): - if isinstance(other, time): - return self._cmp(other) > 0 - else: - _cmperror(self, other) - - def _cmp(self, other, allow_mixed=False): - assert isinstance(other, time) - mytz = self._tzinfo - ottz = other._tzinfo - myoff = otoff = None - - if mytz is ottz: - base_compare = True - else: - myoff = self.utcoffset() - otoff = other.utcoffset() - base_compare = myoff == otoff - - if base_compare: - return _cmp((self._hour, self._minute, self._second, - self._microsecond), - (other._hour, other._minute, other._second, - other._microsecond)) - if myoff is None or otoff is None: - if allow_mixed: - return 2 # arbitrary non-zero value - else: - raise TypeError("cannot compare naive and aware times") - myhhmm = self._hour * 60 + self._minute - myoff//timedelta(minutes=1) - othhmm = other._hour * 60 + other._minute - otoff//timedelta(minutes=1) - return _cmp((myhhmm, self._second, self._microsecond), - (othhmm, other._second, other._microsecond)) - - def __hash__(self): - """Hash.""" - tzoff = self.utcoffset() - if not tzoff: # zero or None - return hash(self._getstate()[0]) - h, m = divmod(timedelta(hours=self.hour, minutes=self.minute) - tzoff, - timedelta(hours=1)) - assert not m % timedelta(minutes=1), "whole minute" - m //= timedelta(minutes=1) - if 0 <= h < 24: - return hash(time(h, m, self.second, self.microsecond)) - return hash((h, m, self.second, self.microsecond)) - - # Conversion to string - - def _tzstr(self, sep=":"): - """Return formatted timezone offset (+xx:xx) or None.""" - off = self.utcoffset() - if off is not None: - if off.days < 0: - sign = "-" - off = -off - else: - sign = "+" - hh, mm = divmod(off, timedelta(hours=1)) - assert not mm % timedelta(minutes=1), "whole minute" - mm //= timedelta(minutes=1) - assert 0 <= hh < 24 - off = "%s%02d%s%02d" % (sign, hh, sep, mm) - return off - - def __repr__(self): - """Convert to formal string, for repr().""" - if self._microsecond != 0: - s = ", %d, %d" % (self._second, self._microsecond) - elif self._second != 0: - s = ", %d" % self._second - else: - s = "" - s= "%s(%d, %d%s)" % ('datetime.' + self.__class__.__name__, - self._hour, self._minute, s) - if self._tzinfo is not None: - assert s[-1:] == ")" - s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" - return s - - def isoformat(self): - """Return the time formatted according to ISO. - - This is 'HH:MM:SS.mmmmmm+zz:zz', or 'HH:MM:SS+zz:zz' if - self.microsecond == 0. - """ - s = _format_time(self._hour, self._minute, self._second, - self._microsecond) - tz = self._tzstr() - if tz: - s += tz - return s - - __str__ = isoformat - - def strftime(self, fmt): - """Format using strftime(). The date part of the timestamp passed - to underlying strftime should not be used. - """ - # The year must be >= 1000 else Python's strftime implementation - # can raise a bogus exception. - timetuple = (1900, 1, 1, - self._hour, self._minute, self._second, - 0, 1, -1) - return _wrap_strftime(self, fmt, timetuple) - - def __format__(self, fmt): - if len(fmt) != 0: - return self.strftime(fmt) - return str(self) - - # Timezone functions - - def utcoffset(self): - """Return the timezone offset in minutes east of UTC (negative west of - UTC).""" - if self._tzinfo is None: - return None - offset = self._tzinfo.utcoffset(None) - _check_utc_offset("utcoffset", offset) - return offset - - def tzname(self): - """Return the timezone name. - - Note that the name is 100% informational -- there's no requirement that - it mean anything in particular. For example, "GMT", "UTC", "-500", - "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies. - """ - if self._tzinfo is None: - return None - name = self._tzinfo.tzname(None) - _check_tzname(name) - return name - - def dst(self): - """Return 0 if DST is not in effect, or the DST offset (in minutes - eastward) if DST is in effect. - - This is purely informational; the DST offset has already been added to - the UTC offset returned by utcoffset() if applicable, so there's no - need to consult dst() unless you're interested in displaying the DST - info. - """ - if self._tzinfo is None: - return None - offset = self._tzinfo.dst(None) - _check_utc_offset("dst", offset) - return offset - - def replace(self, hour=None, minute=None, second=None, microsecond=None, - tzinfo=True): - """Return a new time with new values for the specified fields.""" - if hour is None: - hour = self.hour - if minute is None: - minute = self.minute - if second is None: - second = self.second - if microsecond is None: - microsecond = self.microsecond - if tzinfo is True: - tzinfo = self.tzinfo - _check_time_fields(hour, minute, second, microsecond) - _check_tzinfo_arg(tzinfo) - return time(hour, minute, second, microsecond, tzinfo) - - def __bool__(self): - if self.second or self.microsecond: - return True - offset = self.utcoffset() or timedelta(0) - return timedelta(hours=self.hour, minutes=self.minute) != offset - - # Pickle support. - - def _getstate(self): - us2, us3 = divmod(self._microsecond, 256) - us1, us2 = divmod(us2, 256) - basestate = bytes([self._hour, self._minute, self._second, - us1, us2, us3]) - if self._tzinfo is None: - return (basestate,) - else: - return (basestate, self._tzinfo) - - def __setstate(self, string, tzinfo): - if len(string) != 6 or string[0] >= 24: - raise TypeError("an integer is required") - (self._hour, self._minute, self._second, - us1, us2, us3) = string - self._microsecond = (((us1 << 8) | us2) << 8) | us3 - if tzinfo is None or isinstance(tzinfo, _tzinfo_class): - self._tzinfo = tzinfo - else: - raise TypeError("bad tzinfo state arg %r" % tzinfo) - - def __reduce__(self): - return (time, self._getstate()) - -_time_class = time # so functions w/ args named "time" can get at the class - -time.min = time(0, 0, 0) -time.max = time(23, 59, 59, 999999) -time.resolution = timedelta(microseconds=1) - -class datetime(date): - """datetime(year, month, day[, hour[, minute[, second[, microsecond[,tzinfo]]]]]) - - The year, month and day arguments are required. tzinfo may be None, or an - instance of a tzinfo subclass. The remaining arguments may be ints. - """ - - __slots__ = date.__slots__ + ( - '_hour', '_minute', '_second', - '_microsecond', '_tzinfo') - def __new__(cls, year, month=None, day=None, hour=0, minute=0, second=0, - microsecond=0, tzinfo=None): - if isinstance(year, bytes) and len(year) == 10: - # Pickle support - self = date.__new__(cls, year[:4]) - self.__setstate(year, month) - return self - _check_tzinfo_arg(tzinfo) - _check_time_fields(hour, minute, second, microsecond) - self = date.__new__(cls, year, month, day) - self._hour = hour - self._minute = minute - self._second = second - self._microsecond = microsecond - self._tzinfo = tzinfo - return self - - # Read-only field accessors - @property - def hour(self): - """hour (0-23)""" - return self._hour - - @property - def minute(self): - """minute (0-59)""" - return self._minute - - @property - def second(self): - """second (0-59)""" - return self._second - - @property - def microsecond(self): - """microsecond (0-999999)""" - return self._microsecond - - @property - def tzinfo(self): - """timezone info object""" - return self._tzinfo - - @classmethod - def fromtimestamp(cls, t, tz=None): - """Construct a datetime from a POSIX timestamp (like time.time()). - - A timezone info object may be passed in as well. - """ - - _check_tzinfo_arg(tz) - - converter = _time.localtime if tz is None else _time.gmtime - - t, frac = divmod(t, 1.0) - us = int(frac * 1e6) - - # If timestamp is less than one microsecond smaller than a - # full second, us can be rounded up to 1000000. In this case, - # roll over to seconds, otherwise, ValueError is raised - # by the constructor. - if us == 1000000: - t += 1 - us = 0 - y, m, d, hh, mm, ss, weekday, jday, dst = converter(t) - ss = min(ss, 59) # clamp out leap seconds if the platform has them - result = cls(y, m, d, hh, mm, ss, us, tz) - if tz is not None: - result = tz.fromutc(result) - return result - - @classmethod - def utcfromtimestamp(cls, t): - "Construct a UTC datetime from a POSIX timestamp (like time.time())." - t, frac = divmod(t, 1.0) - us = int(frac * 1e6) - - # If timestamp is less than one microsecond smaller than a - # full second, us can be rounded up to 1000000. In this case, - # roll over to seconds, otherwise, ValueError is raised - # by the constructor. - if us == 1000000: - t += 1 - us = 0 - y, m, d, hh, mm, ss, weekday, jday, dst = _time.gmtime(t) - ss = min(ss, 59) # clamp out leap seconds if the platform has them - return cls(y, m, d, hh, mm, ss, us) - - # XXX This is supposed to do better than we *can* do by using time.time(), - # XXX if the platform supports a more accurate way. The C implementation - # XXX uses gettimeofday on platforms that have it, but that isn't - # XXX available from Python. So now() may return different results - # XXX across the implementations. - @classmethod - def now(cls, tz=None): - "Construct a datetime from time.time() and optional time zone info." - t = _time.time() - return cls.fromtimestamp(t, tz) - - @classmethod - def utcnow(cls): - "Construct a UTC datetime from time.time()." - t = _time.time() - return cls.utcfromtimestamp(t) - - @classmethod - def combine(cls, date, time): - "Construct a datetime from a given date and a given time." - if not isinstance(date, _date_class): - raise TypeError("date argument must be a date instance") - if not isinstance(time, _time_class): - raise TypeError("time argument must be a time instance") - return cls(date.year, date.month, date.day, - time.hour, time.minute, time.second, time.microsecond, - time.tzinfo) - - def timetuple(self): - "Return local time tuple compatible with time.localtime()." - dst = self.dst() - if dst is None: - dst = -1 - elif dst: - dst = 1 - else: - dst = 0 - return _build_struct_time(self.year, self.month, self.day, - self.hour, self.minute, self.second, - dst) - - def timestamp(self): - "Return POSIX timestamp as float" - if self._tzinfo is None: - return _time.mktime((self.year, self.month, self.day, - self.hour, self.minute, self.second, - -1, -1, -1)) + self.microsecond / 1e6 - else: - return (self - _EPOCH).total_seconds() - - def utctimetuple(self): - "Return UTC time tuple compatible with time.gmtime()." - offset = self.utcoffset() - if offset: - self -= offset - y, m, d = self.year, self.month, self.day - hh, mm, ss = self.hour, self.minute, self.second - return _build_struct_time(y, m, d, hh, mm, ss, 0) - - def date(self): - "Return the date part." - return date(self._year, self._month, self._day) - - def time(self): - "Return the time part, with tzinfo None." - return time(self.hour, self.minute, self.second, self.microsecond) - - def timetz(self): - "Return the time part, with same tzinfo." - return time(self.hour, self.minute, self.second, self.microsecond, - self._tzinfo) - - def replace(self, year=None, month=None, day=None, hour=None, - minute=None, second=None, microsecond=None, tzinfo=True): - """Return a new datetime with new values for the specified fields.""" - if year is None: - year = self.year - if month is None: - month = self.month - if day is None: - day = self.day - if hour is None: - hour = self.hour - if minute is None: - minute = self.minute - if second is None: - second = self.second - if microsecond is None: - microsecond = self.microsecond - if tzinfo is True: - tzinfo = self.tzinfo - _check_date_fields(year, month, day) - _check_time_fields(hour, minute, second, microsecond) - _check_tzinfo_arg(tzinfo) - return datetime(year, month, day, hour, minute, second, - microsecond, tzinfo) - - def astimezone(self, tz=None): - if tz is None: - if self.tzinfo is None: - raise ValueError("astimezone() requires an aware datetime") - ts = (self - _EPOCH) // timedelta(seconds=1) - localtm = _time.localtime(ts) - local = datetime(*localtm[:6]) - try: - # Extract TZ data if available - gmtoff = localtm.tm_gmtoff - zone = localtm.tm_zone - except AttributeError: - # Compute UTC offset and compare with the value implied - # by tm_isdst. If the values match, use the zone name - # implied by tm_isdst. - delta = local - datetime(*_time.gmtime(ts)[:6]) - dst = _time.daylight and localtm.tm_isdst > 0 - gmtoff = -(_time.altzone if dst else _time.timezone) - if delta == timedelta(seconds=gmtoff): - tz = timezone(delta, _time.tzname[dst]) - else: - tz = timezone(delta) - else: - tz = timezone(timedelta(seconds=gmtoff), zone) - - elif not isinstance(tz, tzinfo): - raise TypeError("tz argument must be an instance of tzinfo") - - mytz = self.tzinfo - if mytz is None: - raise ValueError("astimezone() requires an aware datetime") - - if tz is mytz: - return self - - # Convert self to UTC, and attach the new time zone object. - myoffset = self.utcoffset() - if myoffset is None: - raise ValueError("astimezone() requires an aware datetime") - utc = (self - myoffset).replace(tzinfo=tz) - - # Convert from UTC to tz's local time. - return tz.fromutc(utc) - - # Ways to produce a string. - - def ctime(self): - "Return ctime() style string." - weekday = self.toordinal() % 7 or 7 - return "%s %s %2d %02d:%02d:%02d %04d" % ( - _DAYNAMES[weekday], - _MONTHNAMES[self._month], - self._day, - self._hour, self._minute, self._second, - self._year) - - def isoformat(self, sep='T'): - """Return the time formatted according to ISO. - - This is 'YYYY-MM-DD HH:MM:SS.mmmmmm', or 'YYYY-MM-DD HH:MM:SS' if - self.microsecond == 0. - - If self.tzinfo is not None, the UTC offset is also attached, giving - 'YYYY-MM-DD HH:MM:SS.mmmmmm+HH:MM' or 'YYYY-MM-DD HH:MM:SS+HH:MM'. - - Optional argument sep specifies the separator between date and - time, default 'T'. - """ - s = ("%04d-%02d-%02d%c" % (self._year, self._month, self._day, - sep) + - _format_time(self._hour, self._minute, self._second, - self._microsecond)) - off = self.utcoffset() - if off is not None: - if off.days < 0: - sign = "-" - off = -off - else: - sign = "+" - hh, mm = divmod(off, timedelta(hours=1)) - assert not mm % timedelta(minutes=1), "whole minute" - mm //= timedelta(minutes=1) - s += "%s%02d:%02d" % (sign, hh, mm) - return s - - def __repr__(self): - """Convert to formal string, for repr().""" - L = [self._year, self._month, self._day, # These are never zero - self._hour, self._minute, self._second, self._microsecond] - if L[-1] == 0: - del L[-1] - if L[-1] == 0: - del L[-1] - s = ", ".join(map(str, L)) - s = "%s(%s)" % ('datetime.' + self.__class__.__name__, s) - if self._tzinfo is not None: - assert s[-1:] == ")" - s = s[:-1] + ", tzinfo=%r" % self._tzinfo + ")" - return s - - def __str__(self): - "Convert to string, for str()." - return self.isoformat(sep=' ') - - @classmethod - def strptime(cls, date_string, format): - 'string, format -> new datetime parsed from a string (like time.strptime()).' - import _strptime - return _strptime._strptime_datetime(cls, date_string, format) - - def utcoffset(self): - """Return the timezone offset in minutes east of UTC (negative west of - UTC).""" - if self._tzinfo is None: - return None - offset = self._tzinfo.utcoffset(self) - _check_utc_offset("utcoffset", offset) - return offset - - def tzname(self): - """Return the timezone name. - - Note that the name is 100% informational -- there's no requirement that - it mean anything in particular. For example, "GMT", "UTC", "-500", - "-5:00", "EDT", "US/Eastern", "America/New York" are all valid replies. - """ - name = _call_tzinfo_method(self._tzinfo, "tzname", self) - _check_tzname(name) - return name - - def dst(self): - """Return 0 if DST is not in effect, or the DST offset (in minutes - eastward) if DST is in effect. - - This is purely informational; the DST offset has already been added to - the UTC offset returned by utcoffset() if applicable, so there's no - need to consult dst() unless you're interested in displaying the DST - info. - """ - if self._tzinfo is None: - return None - offset = self._tzinfo.dst(self) - _check_utc_offset("dst", offset) - return offset - - # Comparisons of datetime objects with other. - - def __eq__(self, other): - if isinstance(other, datetime): - return self._cmp(other, allow_mixed=True) == 0 - elif not isinstance(other, date): - return NotImplemented - else: - return False - - def __ne__(self, other): - if isinstance(other, datetime): - return self._cmp(other, allow_mixed=True) != 0 - elif not isinstance(other, date): - return NotImplemented - else: - return True - - def __le__(self, other): - if isinstance(other, datetime): - return self._cmp(other) <= 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def __lt__(self, other): - if isinstance(other, datetime): - return self._cmp(other) < 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def __ge__(self, other): - if isinstance(other, datetime): - return self._cmp(other) >= 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def __gt__(self, other): - if isinstance(other, datetime): - return self._cmp(other) > 0 - elif not isinstance(other, date): - return NotImplemented - else: - _cmperror(self, other) - - def _cmp(self, other, allow_mixed=False): - assert isinstance(other, datetime) - mytz = self._tzinfo - ottz = other._tzinfo - myoff = otoff = None - - if mytz is ottz: - base_compare = True - else: - myoff = self.utcoffset() - otoff = other.utcoffset() - base_compare = myoff == otoff - - if base_compare: - return _cmp((self._year, self._month, self._day, - self._hour, self._minute, self._second, - self._microsecond), - (other._year, other._month, other._day, - other._hour, other._minute, other._second, - other._microsecond)) - if myoff is None or otoff is None: - if allow_mixed: - return 2 # arbitrary non-zero value - else: - raise TypeError("cannot compare naive and aware datetimes") - # XXX What follows could be done more efficiently... - diff = self - other # this will take offsets into account - if diff.days < 0: - return -1 - return diff and 1 or 0 - - def __add__(self, other): - "Add a datetime and a timedelta." - if not isinstance(other, timedelta): - return NotImplemented - delta = timedelta(self.toordinal(), - hours=self._hour, - minutes=self._minute, - seconds=self._second, - microseconds=self._microsecond) - delta += other - hour, rem = divmod(delta.seconds, 3600) - minute, second = divmod(rem, 60) - if 0 < delta.days <= _MAXORDINAL: - return datetime.combine(date.fromordinal(delta.days), - time(hour, minute, second, - delta.microseconds, - tzinfo=self._tzinfo)) - raise OverflowError("result out of range") - - __radd__ = __add__ - - def __sub__(self, other): - "Subtract two datetimes, or a datetime and a timedelta." - if not isinstance(other, datetime): - if isinstance(other, timedelta): - return self + -other - return NotImplemented - - days1 = self.toordinal() - days2 = other.toordinal() - secs1 = self._second + self._minute * 60 + self._hour * 3600 - secs2 = other._second + other._minute * 60 + other._hour * 3600 - base = timedelta(days1 - days2, - secs1 - secs2, - self._microsecond - other._microsecond) - if self._tzinfo is other._tzinfo: - return base - myoff = self.utcoffset() - otoff = other.utcoffset() - if myoff == otoff: - return base - if myoff is None or otoff is None: - raise TypeError("cannot mix naive and timezone-aware time") - return base + otoff - myoff - - def __hash__(self): - tzoff = self.utcoffset() - if tzoff is None: - return hash(self._getstate()[0]) - days = _ymd2ord(self.year, self.month, self.day) - seconds = self.hour * 3600 + self.minute * 60 + self.second - return hash(timedelta(days, seconds, self.microsecond) - tzoff) - - # Pickle support. - - def _getstate(self): - yhi, ylo = divmod(self._year, 256) - us2, us3 = divmod(self._microsecond, 256) - us1, us2 = divmod(us2, 256) - basestate = bytes([yhi, ylo, self._month, self._day, - self._hour, self._minute, self._second, - us1, us2, us3]) - if self._tzinfo is None: - return (basestate,) - else: - return (basestate, self._tzinfo) - - def __setstate(self, string, tzinfo): - (yhi, ylo, self._month, self._day, self._hour, - self._minute, self._second, us1, us2, us3) = string - self._year = yhi * 256 + ylo - self._microsecond = (((us1 << 8) | us2) << 8) | us3 - if tzinfo is None or isinstance(tzinfo, _tzinfo_class): - self._tzinfo = tzinfo - else: - raise TypeError("bad tzinfo state arg %r" % tzinfo) - - def __reduce__(self): - return (self.__class__, self._getstate()) - - -datetime.min = datetime(1, 1, 1) -datetime.max = datetime(9999, 12, 31, 23, 59, 59, 999999) -datetime.resolution = timedelta(microseconds=1) - - -def _isoweek1monday(year): - # Helper to calculate the day number of the Monday starting week 1 - # XXX This could be done more efficiently - THURSDAY = 3 - firstday = _ymd2ord(year, 1, 1) - firstweekday = (firstday + 6) % 7 # See weekday() above - week1monday = firstday - firstweekday - if firstweekday > THURSDAY: - week1monday += 7 - return week1monday - -class timezone(tzinfo): - __slots__ = '_offset', '_name' - - # Sentinel value to disallow None - _Omitted = object() - def __new__(cls, offset, name=_Omitted): - if not isinstance(offset, timedelta): - raise TypeError("offset must be a timedelta") - if name is cls._Omitted: - if not offset: - return cls.utc - name = None - elif not isinstance(name, str): - ### - # For Python-Future: - if PY2 and isinstance(name, native_str): - name = name.decode() - else: - raise TypeError("name must be a string") - ### - if not cls._minoffset <= offset <= cls._maxoffset: - raise ValueError("offset must be a timedelta" - " strictly between -timedelta(hours=24) and" - " timedelta(hours=24).") - if (offset.microseconds != 0 or - offset.seconds % 60 != 0): - raise ValueError("offset must be a timedelta" - " representing a whole number of minutes") - return cls._create(offset, name) - - @classmethod - def _create(cls, offset, name=None): - self = tzinfo.__new__(cls) - self._offset = offset - self._name = name - return self - - def __getinitargs__(self): - """pickle support""" - if self._name is None: - return (self._offset,) - return (self._offset, self._name) - - def __eq__(self, other): - if type(other) != timezone: - return False - return self._offset == other._offset - - def __hash__(self): - return hash(self._offset) - - def __repr__(self): - """Convert to formal string, for repr(). - - >>> tz = timezone.utc - >>> repr(tz) - 'datetime.timezone.utc' - >>> tz = timezone(timedelta(hours=-5), 'EST') - >>> repr(tz) - "datetime.timezone(datetime.timedelta(-1, 68400), 'EST')" - """ - if self is self.utc: - return 'datetime.timezone.utc' - if self._name is None: - return "%s(%r)" % ('datetime.' + self.__class__.__name__, - self._offset) - return "%s(%r, %r)" % ('datetime.' + self.__class__.__name__, - self._offset, self._name) - - def __str__(self): - return self.tzname(None) - - def utcoffset(self, dt): - if isinstance(dt, datetime) or dt is None: - return self._offset - raise TypeError("utcoffset() argument must be a datetime instance" - " or None") - - def tzname(self, dt): - if isinstance(dt, datetime) or dt is None: - if self._name is None: - return self._name_from_offset(self._offset) - return self._name - raise TypeError("tzname() argument must be a datetime instance" - " or None") - - def dst(self, dt): - if isinstance(dt, datetime) or dt is None: - return None - raise TypeError("dst() argument must be a datetime instance" - " or None") - - def fromutc(self, dt): - if isinstance(dt, datetime): - if dt.tzinfo is not self: - raise ValueError("fromutc: dt.tzinfo " - "is not self") - return dt + self._offset - raise TypeError("fromutc() argument must be a datetime instance" - " or None") - - _maxoffset = timedelta(hours=23, minutes=59) - _minoffset = -_maxoffset - - @staticmethod - def _name_from_offset(delta): - if delta < timedelta(0): - sign = '-' - delta = -delta - else: - sign = '+' - hours, rest = divmod(delta, timedelta(hours=1)) - minutes = rest // timedelta(minutes=1) - return 'UTC{}{:02d}:{:02d}'.format(sign, hours, minutes) - -timezone.utc = timezone._create(timedelta(0)) -timezone.min = timezone._create(timezone._minoffset) -timezone.max = timezone._create(timezone._maxoffset) -_EPOCH = datetime(1970, 1, 1, tzinfo=timezone.utc) -""" -Some time zone algebra. For a datetime x, let - x.n = x stripped of its timezone -- its naive time. - x.o = x.utcoffset(), and assuming that doesn't raise an exception or - return None - x.d = x.dst(), and assuming that doesn't raise an exception or - return None - x.s = x's standard offset, x.o - x.d - -Now some derived rules, where k is a duration (timedelta). - -1. x.o = x.s + x.d - This follows from the definition of x.s. - -2. If x and y have the same tzinfo member, x.s = y.s. - This is actually a requirement, an assumption we need to make about - sane tzinfo classes. - -3. The naive UTC time corresponding to x is x.n - x.o. - This is again a requirement for a sane tzinfo class. - -4. (x+k).s = x.s - This follows from #2, and that datimetimetz+timedelta preserves tzinfo. - -5. (x+k).n = x.n + k - Again follows from how arithmetic is defined. - -Now we can explain tz.fromutc(x). Let's assume it's an interesting case -(meaning that the various tzinfo methods exist, and don't blow up or return -None when called). - -The function wants to return a datetime y with timezone tz, equivalent to x. -x is already in UTC. - -By #3, we want - - y.n - y.o = x.n [1] - -The algorithm starts by attaching tz to x.n, and calling that y. So -x.n = y.n at the start. Then it wants to add a duration k to y, so that [1] -becomes true; in effect, we want to solve [2] for k: - - (y+k).n - (y+k).o = x.n [2] - -By #1, this is the same as - - (y+k).n - ((y+k).s + (y+k).d) = x.n [3] - -By #5, (y+k).n = y.n + k, which equals x.n + k because x.n=y.n at the start. -Substituting that into [3], - - x.n + k - (y+k).s - (y+k).d = x.n; the x.n terms cancel, leaving - k - (y+k).s - (y+k).d = 0; rearranging, - k = (y+k).s - (y+k).d; by #4, (y+k).s == y.s, so - k = y.s - (y+k).d - -On the RHS, (y+k).d can't be computed directly, but y.s can be, and we -approximate k by ignoring the (y+k).d term at first. Note that k can't be -very large, since all offset-returning methods return a duration of magnitude -less than 24 hours. For that reason, if y is firmly in std time, (y+k).d must -be 0, so ignoring it has no consequence then. - -In any case, the new value is - - z = y + y.s [4] - -It's helpful to step back at look at [4] from a higher level: it's simply -mapping from UTC to tz's standard time. - -At this point, if - - z.n - z.o = x.n [5] - -we have an equivalent time, and are almost done. The insecurity here is -at the start of daylight time. Picture US Eastern for concreteness. The wall -time jumps from 1:59 to 3:00, and wall hours of the form 2:MM don't make good -sense then. The docs ask that an Eastern tzinfo class consider such a time to -be EDT (because it's "after 2"), which is a redundant spelling of 1:MM EST -on the day DST starts. We want to return the 1:MM EST spelling because that's -the only spelling that makes sense on the local wall clock. - -In fact, if [5] holds at this point, we do have the standard-time spelling, -but that takes a bit of proof. We first prove a stronger result. What's the -difference between the LHS and RHS of [5]? Let - - diff = x.n - (z.n - z.o) [6] - -Now - z.n = by [4] - (y + y.s).n = by #5 - y.n + y.s = since y.n = x.n - x.n + y.s = since z and y are have the same tzinfo member, - y.s = z.s by #2 - x.n + z.s - -Plugging that back into [6] gives - - diff = - x.n - ((x.n + z.s) - z.o) = expanding - x.n - x.n - z.s + z.o = cancelling - - z.s + z.o = by #2 - z.d - -So diff = z.d. - -If [5] is true now, diff = 0, so z.d = 0 too, and we have the standard-time -spelling we wanted in the endcase described above. We're done. Contrarily, -if z.d = 0, then we have a UTC equivalent, and are also done. - -If [5] is not true now, diff = z.d != 0, and z.d is the offset we need to -add to z (in effect, z is in tz's standard time, and we need to shift the -local clock into tz's daylight time). - -Let - - z' = z + z.d = z + diff [7] - -and we can again ask whether - - z'.n - z'.o = x.n [8] - -If so, we're done. If not, the tzinfo class is insane, according to the -assumptions we've made. This also requires a bit of proof. As before, let's -compute the difference between the LHS and RHS of [8] (and skipping some of -the justifications for the kinds of substitutions we've done several times -already): - - diff' = x.n - (z'.n - z'.o) = replacing z'.n via [7] - x.n - (z.n + diff - z'.o) = replacing diff via [6] - x.n - (z.n + x.n - (z.n - z.o) - z'.o) = - x.n - z.n - x.n + z.n - z.o + z'.o = cancel x.n - - z.n + z.n - z.o + z'.o = cancel z.n - - z.o + z'.o = #1 twice - -z.s - z.d + z'.s + z'.d = z and z' have same tzinfo - z'.d - z.d - -So z' is UTC-equivalent to x iff z'.d = z.d at this point. If they are equal, -we've found the UTC-equivalent so are done. In fact, we stop with [7] and -return z', not bothering to compute z'.d. - -How could z.d and z'd differ? z' = z + z.d [7], so merely moving z' by -a dst() offset, and starting *from* a time already in DST (we know z.d != 0), -would have to change the result dst() returns: we start in DST, and moving -a little further into it takes us out of DST. - -There isn't a sane case where this can happen. The closest it gets is at -the end of DST, where there's an hour in UTC with no spelling in a hybrid -tzinfo class. In US Eastern, that's 5:MM UTC = 0:MM EST = 1:MM EDT. During -that hour, on an Eastern clock 1:MM is taken as being in standard time (6:MM -UTC) because the docs insist on that, but 0:MM is taken as being in daylight -time (4:MM UTC). There is no local time mapping to 5:MM UTC. The local -clock jumps from 1:59 back to 1:00 again, and repeats the 1:MM hour in -standard time. Since that's what the local clock *does*, we want to map both -UTC hours 5:MM and 6:MM to 1:MM Eastern. The result is ambiguous -in local time, but so it goes -- it's the way the local clock works. - -When x = 5:MM UTC is the input to this algorithm, x.o=0, y.o=-5 and y.d=0, -so z=0:MM. z.d=60 (minutes) then, so [5] doesn't hold and we keep going. -z' = z + z.d = 1:MM then, and z'.d=0, and z'.d - z.d = -60 != 0 so [8] -(correctly) concludes that z' is not UTC-equivalent to x. - -Because we know z.d said z was in daylight time (else [5] would have held and -we would have stopped then), and we know z.d != z'.d (else [8] would have held -and we have stopped then), and there are only 2 possible values dst() can -return in Eastern, it follows that z'.d must be 0 (which it is in the example, -but the reasoning doesn't depend on the example -- it depends on there being -two possible dst() outcomes, one zero and the other non-zero). Therefore -z' must be in standard time, and is the spelling we want in this case. - -Note again that z' is not UTC-equivalent as far as the hybrid tzinfo class is -concerned (because it takes z' as being in standard time rather than the -daylight time we intend here), but returning it gives the real-life "local -clock repeats an hour" behavior when mapping the "unspellable" UTC hour into -tz. - -When the input is 6:MM, z=1:MM and z.d=0, and we stop at once, again with -the 1:MM standard time spelling we want. - -So how can this break? One of the assumptions must be violated. Two -possibilities: - -1) [2] effectively says that y.s is invariant across all y belong to a given - time zone. This isn't true if, for political reasons or continental drift, - a region decides to change its base offset from UTC. - -2) There may be versions of "double daylight" time where the tail end of - the analysis gives up a step too early. I haven't thought about that - enough to say. - -In any case, it's clear that the default fromutc() is strong enough to handle -"almost all" time zones: so long as the standard offset is invariant, it -doesn't matter if daylight time transition points change from year to year, or -if daylight time is skipped in some years; it doesn't matter how large or -small dst() may get within its bounds; and it doesn't even matter if some -perverse time zone returns a negative dst()). So a breaking case must be -pretty bizarre, and a tzinfo subclass can override fromutc() if it is. -""" -try: - from _datetime import * -except ImportError: - pass -else: - # Clean up unused names - del (_DAYNAMES, _DAYS_BEFORE_MONTH, _DAYS_IN_MONTH, - _DI100Y, _DI400Y, _DI4Y, _MAXORDINAL, _MONTHNAMES, - _build_struct_time, _call_tzinfo_method, _check_date_fields, - _check_time_fields, _check_tzinfo_arg, _check_tzname, - _check_utc_offset, _cmp, _cmperror, _date_class, _days_before_month, - _days_before_year, _days_in_month, _format_time, _is_leap, - _isoweek1monday, _math, _ord2ymd, _time, _time_class, _tzinfo_class, - _wrap_strftime, _ymd2ord) - # XXX Since import * above excludes names that start with _, - # docstring does not get overwritten. In the future, it may be - # appropriate to maintain a single module level docstring and - # remove the following line. - from _datetime import __doc__ diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/base64mime.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/base64mime.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/base64mime.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/base64mime.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Base64 content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode arbitrary 8-bit data using the three 8-bit bytes in four 7-bit -characters encoding known as Base64. - -It is used in the MIME standards for email to attach images, audio, and text -using some 8-bit character sets to messages. - -This module provides an interface to encode and decode both headers and bodies -with Base64 encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:, From:, Cc:, etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character conversion -necessary for proper internationalized headers; it only does dumb encoding and -decoding. To deal with the various line wrapping issues, use the email.header -module. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import range -from future.builtins import bytes - -__all__ = [ - 'body_decode', - 'body_encode', - 'decode', - 'decodestring', - 'header_encode', - 'header_length', - ] - - -from base64 import b64encode -from binascii import b2a_base64, a2b_base64 - -CRLF = '\r\n' -NL = '\n' -EMPTYSTRING = '' - -# See also Charset.py -MISC_LEN = 7 - - -# Helpers -def header_length(bytearray): - """Return the length of s when it is encoded with base64.""" - groups_of_3, leftover = divmod(len(bytearray), 3) - # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. - n = groups_of_3 * 4 - if leftover: - n += 4 - return n - - -def header_encode(header_bytes, charset='iso-8859-1'): - """Encode a single header line with Base64 encoding in a given charset. - - charset names the character set to use to encode the header. It defaults - to iso-8859-1. Base64 encoding is defined in RFC 2045. - """ - if not header_bytes: - return "" - if isinstance(header_bytes, str): - header_bytes = header_bytes.encode(charset) - encoded = b64encode(header_bytes).decode("ascii") - return '=?%s?b?%s?=' % (charset, encoded) - - -def body_encode(s, maxlinelen=76, eol=NL): - r"""Encode a string with base64. - - Each line will be wrapped at, at most, maxlinelen characters (defaults to - 76 characters). - - Each line of encoded text will end with eol, which defaults to "\n". Set - this to "\r\n" if you will be using the result of this function directly - in an email. - """ - if not s: - return s - - encvec = [] - max_unencoded = maxlinelen * 3 // 4 - for i in range(0, len(s), max_unencoded): - # BAW: should encode() inherit b2a_base64()'s dubious behavior in - # adding a newline to the encoded string? - enc = b2a_base64(s[i:i + max_unencoded]).decode("ascii") - if enc.endswith(NL) and eol != NL: - enc = enc[:-1] + eol - encvec.append(enc) - return EMPTYSTRING.join(encvec) - - -def decode(string): - """Decode a raw base64 string, returning a bytes object. - - This function does not parse a full MIME header value encoded with - base64 (like =?iso-8895-1?b?bmloISBuaWgh?=) -- please use the high - level email.header class for that functionality. - """ - if not string: - return bytes() - elif isinstance(string, str): - return a2b_base64(string.encode('raw-unicode-escape')) - else: - return a2b_base64(string) - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/charset.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/charset.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/charset.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/charset.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,409 +0,0 @@ -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import str -from future.builtins import next - -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -__all__ = [ - 'Charset', - 'add_alias', - 'add_charset', - 'add_codec', - ] - -from functools import partial - -from future.backports import email -from future.backports.email import errors -from future.backports.email.encoders import encode_7or8bit - - -# Flags for types of header encodings -QP = 1 # Quoted-Printable -BASE64 = 2 # Base64 -SHORTEST = 3 # the shorter of QP and base64, but only for headers - -# In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7 -RFC2047_CHROME_LEN = 7 - -DEFAULT_CHARSET = 'us-ascii' -UNKNOWN8BIT = 'unknown-8bit' -EMPTYSTRING = '' - - -# Defaults -CHARSETS = { - # input header enc body enc output conv - 'iso-8859-1': (QP, QP, None), - 'iso-8859-2': (QP, QP, None), - 'iso-8859-3': (QP, QP, None), - 'iso-8859-4': (QP, QP, None), - # iso-8859-5 is Cyrillic, and not especially used - # iso-8859-6 is Arabic, also not particularly used - # iso-8859-7 is Greek, QP will not make it readable - # iso-8859-8 is Hebrew, QP will not make it readable - 'iso-8859-9': (QP, QP, None), - 'iso-8859-10': (QP, QP, None), - # iso-8859-11 is Thai, QP will not make it readable - 'iso-8859-13': (QP, QP, None), - 'iso-8859-14': (QP, QP, None), - 'iso-8859-15': (QP, QP, None), - 'iso-8859-16': (QP, QP, None), - 'windows-1252':(QP, QP, None), - 'viscii': (QP, QP, None), - 'us-ascii': (None, None, None), - 'big5': (BASE64, BASE64, None), - 'gb2312': (BASE64, BASE64, None), - 'euc-jp': (BASE64, None, 'iso-2022-jp'), - 'shift_jis': (BASE64, None, 'iso-2022-jp'), - 'iso-2022-jp': (BASE64, None, None), - 'koi8-r': (BASE64, BASE64, None), - 'utf-8': (SHORTEST, BASE64, 'utf-8'), - } - -# Aliases for other commonly-used names for character sets. Map -# them to the real ones used in email. -ALIASES = { - 'latin_1': 'iso-8859-1', - 'latin-1': 'iso-8859-1', - 'latin_2': 'iso-8859-2', - 'latin-2': 'iso-8859-2', - 'latin_3': 'iso-8859-3', - 'latin-3': 'iso-8859-3', - 'latin_4': 'iso-8859-4', - 'latin-4': 'iso-8859-4', - 'latin_5': 'iso-8859-9', - 'latin-5': 'iso-8859-9', - 'latin_6': 'iso-8859-10', - 'latin-6': 'iso-8859-10', - 'latin_7': 'iso-8859-13', - 'latin-7': 'iso-8859-13', - 'latin_8': 'iso-8859-14', - 'latin-8': 'iso-8859-14', - 'latin_9': 'iso-8859-15', - 'latin-9': 'iso-8859-15', - 'latin_10':'iso-8859-16', - 'latin-10':'iso-8859-16', - 'cp949': 'ks_c_5601-1987', - 'euc_jp': 'euc-jp', - 'euc_kr': 'euc-kr', - 'ascii': 'us-ascii', - } - - -# Map charsets to their Unicode codec strings. -CODEC_MAP = { - 'gb2312': 'eucgb2312_cn', - 'big5': 'big5_tw', - # Hack: We don't want *any* conversion for stuff marked us-ascii, as all - # sorts of garbage might be sent to us in the guise of 7-bit us-ascii. - # Let that stuff pass through without conversion to/from Unicode. - 'us-ascii': None, - } - - -# Convenience functions for extending the above mappings -def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): - """Add character set properties to the global registry. - - charset is the input character set, and must be the canonical name of a - character set. - - Optional header_enc and body_enc is either Charset.QP for - quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for - the shortest of qp or base64 encoding, or None for no encoding. SHORTEST - is only valid for header_enc. It describes how message headers and - message bodies in the input charset are to be encoded. Default is no - encoding. - - Optional output_charset is the character set that the output should be - in. Conversions will proceed from input charset, to Unicode, to the - output charset when the method Charset.convert() is called. The default - is to output in the same character set as the input. - - Both input_charset and output_charset must have Unicode codec entries in - the module's charset-to-codec mapping; use add_codec(charset, codecname) - to add codecs the module does not know about. See the codecs module's - documentation for more information. - """ - if body_enc == SHORTEST: - raise ValueError('SHORTEST not allowed for body_enc') - CHARSETS[charset] = (header_enc, body_enc, output_charset) - - -def add_alias(alias, canonical): - """Add a character set alias. - - alias is the alias name, e.g. latin-1 - canonical is the character set's canonical name, e.g. iso-8859-1 - """ - ALIASES[alias] = canonical - - -def add_codec(charset, codecname): - """Add a codec that map characters in the given charset to/from Unicode. - - charset is the canonical name of a character set. codecname is the name - of a Python codec, as appropriate for the second argument to the unicode() - built-in, or to the encode() method of a Unicode string. - """ - CODEC_MAP[charset] = codecname - - -# Convenience function for encoding strings, taking into account -# that they might be unknown-8bit (ie: have surrogate-escaped bytes) -def _encode(string, codec): - string = str(string) - if codec == UNKNOWN8BIT: - return string.encode('ascii', 'surrogateescape') - else: - return string.encode(codec) - - -class Charset(object): - """Map character sets to their email properties. - - This class provides information about the requirements imposed on email - for a specific character set. It also provides convenience routines for - converting between character sets, given the availability of the - applicable codecs. Given a character set, it will do its best to provide - information on how to use that character set in an email in an - RFC-compliant way. - - Certain character sets must be encoded with quoted-printable or base64 - when used in email headers or bodies. Certain character sets must be - converted outright, and are not allowed in email. Instances of this - module expose the following information about a character set: - - input_charset: The initial character set specified. Common aliases - are converted to their `official' email names (e.g. latin_1 - is converted to iso-8859-1). Defaults to 7-bit us-ascii. - - header_encoding: If the character set must be encoded before it can be - used in an email header, this attribute will be set to - Charset.QP (for quoted-printable), Charset.BASE64 (for - base64 encoding), or Charset.SHORTEST for the shortest of - QP or BASE64 encoding. Otherwise, it will be None. - - body_encoding: Same as header_encoding, but describes the encoding for the - mail message's body, which indeed may be different than the - header encoding. Charset.SHORTEST is not allowed for - body_encoding. - - output_charset: Some character sets must be converted before they can be - used in email headers or bodies. If the input_charset is - one of them, this attribute will contain the name of the - charset output will be converted to. Otherwise, it will - be None. - - input_codec: The name of the Python codec used to convert the - input_charset to Unicode. If no conversion codec is - necessary, this attribute will be None. - - output_codec: The name of the Python codec used to convert Unicode - to the output_charset. If no conversion codec is necessary, - this attribute will have the same value as the input_codec. - """ - def __init__(self, input_charset=DEFAULT_CHARSET): - # RFC 2046, $4.1.2 says charsets are not case sensitive. We coerce to - # unicode because its .lower() is locale insensitive. If the argument - # is already a unicode, we leave it at that, but ensure that the - # charset is ASCII, as the standard (RFC XXX) requires. - try: - if isinstance(input_charset, str): - input_charset.encode('ascii') - else: - input_charset = str(input_charset, 'ascii') - except UnicodeError: - raise errors.CharsetError(input_charset) - input_charset = input_charset.lower() - # Set the input charset after filtering through the aliases - self.input_charset = ALIASES.get(input_charset, input_charset) - # We can try to guess which encoding and conversion to use by the - # charset_map dictionary. Try that first, but let the user override - # it. - henc, benc, conv = CHARSETS.get(self.input_charset, - (SHORTEST, BASE64, None)) - if not conv: - conv = self.input_charset - # Set the attributes, allowing the arguments to override the default. - self.header_encoding = henc - self.body_encoding = benc - self.output_charset = ALIASES.get(conv, conv) - # Now set the codecs. If one isn't defined for input_charset, - # guess and try a Unicode codec with the same name as input_codec. - self.input_codec = CODEC_MAP.get(self.input_charset, - self.input_charset) - self.output_codec = CODEC_MAP.get(self.output_charset, - self.output_charset) - - def __str__(self): - return self.input_charset.lower() - - __repr__ = __str__ - - def __eq__(self, other): - return str(self) == str(other).lower() - - def __ne__(self, other): - return not self.__eq__(other) - - def get_body_encoding(self): - """Return the content-transfer-encoding used for body encoding. - - This is either the string `quoted-printable' or `base64' depending on - the encoding used, or it is a function in which case you should call - the function with a single argument, the Message object being - encoded. The function should then set the Content-Transfer-Encoding - header itself to whatever is appropriate. - - Returns "quoted-printable" if self.body_encoding is QP. - Returns "base64" if self.body_encoding is BASE64. - Returns conversion function otherwise. - """ - assert self.body_encoding != SHORTEST - if self.body_encoding == QP: - return 'quoted-printable' - elif self.body_encoding == BASE64: - return 'base64' - else: - return encode_7or8bit - - def get_output_charset(self): - """Return the output character set. - - This is self.output_charset if that is not None, otherwise it is - self.input_charset. - """ - return self.output_charset or self.input_charset - - def header_encode(self, string): - """Header-encode a string by converting it first to bytes. - - The type of encoding (base64 or quoted-printable) will be based on - this charset's `header_encoding`. - - :param string: A unicode string for the header. It must be possible - to encode this string to bytes using the character set's - output codec. - :return: The encoded string, with RFC 2047 chrome. - """ - codec = self.output_codec or 'us-ascii' - header_bytes = _encode(string, codec) - # 7bit/8bit encodings return the string unchanged (modulo conversions) - encoder_module = self._get_encoder(header_bytes) - if encoder_module is None: - return string - return encoder_module.header_encode(header_bytes, codec) - - def header_encode_lines(self, string, maxlengths): - """Header-encode a string by converting it first to bytes. - - This is similar to `header_encode()` except that the string is fit - into maximum line lengths as given by the argument. - - :param string: A unicode string for the header. It must be possible - to encode this string to bytes using the character set's - output codec. - :param maxlengths: Maximum line length iterator. Each element - returned from this iterator will provide the next maximum line - length. This parameter is used as an argument to built-in next() - and should never be exhausted. The maximum line lengths should - not count the RFC 2047 chrome. These line lengths are only a - hint; the splitter does the best it can. - :return: Lines of encoded strings, each with RFC 2047 chrome. - """ - # See which encoding we should use. - codec = self.output_codec or 'us-ascii' - header_bytes = _encode(string, codec) - encoder_module = self._get_encoder(header_bytes) - encoder = partial(encoder_module.header_encode, charset=codec) - # Calculate the number of characters that the RFC 2047 chrome will - # contribute to each line. - charset = self.get_output_charset() - extra = len(charset) + RFC2047_CHROME_LEN - # Now comes the hard part. We must encode bytes but we can't split on - # bytes because some character sets are variable length and each - # encoded word must stand on its own. So the problem is you have to - # encode to bytes to figure out this word's length, but you must split - # on characters. This causes two problems: first, we don't know how - # many octets a specific substring of unicode characters will get - # encoded to, and second, we don't know how many ASCII characters - # those octets will get encoded to. Unless we try it. Which seems - # inefficient. In the interest of being correct rather than fast (and - # in the hope that there will be few encoded headers in any such - # message), brute force it. :( - lines = [] - current_line = [] - maxlen = next(maxlengths) - extra - for character in string: - current_line.append(character) - this_line = EMPTYSTRING.join(current_line) - length = encoder_module.header_length(_encode(this_line, charset)) - if length > maxlen: - # This last character doesn't fit so pop it off. - current_line.pop() - # Does nothing fit on the first line? - if not lines and not current_line: - lines.append(None) - else: - separator = (' ' if lines else '') - joined_line = EMPTYSTRING.join(current_line) - header_bytes = _encode(joined_line, codec) - lines.append(encoder(header_bytes)) - current_line = [character] - maxlen = next(maxlengths) - extra - joined_line = EMPTYSTRING.join(current_line) - header_bytes = _encode(joined_line, codec) - lines.append(encoder(header_bytes)) - return lines - - def _get_encoder(self, header_bytes): - if self.header_encoding == BASE64: - return email.base64mime - elif self.header_encoding == QP: - return email.quoprimime - elif self.header_encoding == SHORTEST: - len64 = email.base64mime.header_length(header_bytes) - lenqp = email.quoprimime.header_length(header_bytes) - if len64 < lenqp: - return email.base64mime - else: - return email.quoprimime - else: - return None - - def body_encode(self, string): - """Body-encode a string by converting it first to bytes. - - The type of encoding (base64 or quoted-printable) will be based on - self.body_encoding. If body_encoding is None, we assume the - output charset is a 7bit encoding, so re-encoding the decoded - string using the ascii codec produces the correct string version - of the content. - """ - if not string: - return string - if self.body_encoding is BASE64: - if isinstance(string, str): - string = string.encode(self.output_charset) - return email.base64mime.body_encode(string) - elif self.body_encoding is QP: - # quopromime.body_encode takes a string, but operates on it as if - # it were a list of byte codes. For a (minimal) history on why - # this is so, see changeset 0cf700464177. To correctly encode a - # character set, then, we must turn it into pseudo bytes via the - # latin1 charset, which will encode any byte as a single code point - # between 0 and 255, which is what body_encode is expecting. - if isinstance(string, str): - string = string.encode(self.output_charset) - string = string.decode('latin1') - return email.quoprimime.body_encode(string) - else: - if isinstance(string, str): - string = string.encode(self.output_charset).decode('ascii') - return string diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_encoded_words.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_encoded_words.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_encoded_words.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_encoded_words.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,232 +0,0 @@ -""" Routines for manipulating RFC2047 encoded words. - -This is currently a package-private API, but will be considered for promotion -to a public API if there is demand. - -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes -from future.builtins import chr -from future.builtins import int -from future.builtins import str - -# An ecoded word looks like this: -# -# =?charset[*lang]?cte?encoded_string?= -# -# for more information about charset see the charset module. Here it is one -# of the preferred MIME charset names (hopefully; you never know when parsing). -# cte (Content Transfer Encoding) is either 'q' or 'b' (ignoring case). In -# theory other letters could be used for other encodings, but in practice this -# (almost?) never happens. There could be a public API for adding entries -# to the CTE tables, but YAGNI for now. 'q' is Quoted Printable, 'b' is -# Base64. The meaning of encoded_string should be obvious. 'lang' is optional -# as indicated by the brackets (they are not part of the syntax) but is almost -# never encountered in practice. -# -# The general interface for a CTE decoder is that it takes the encoded_string -# as its argument, and returns a tuple (cte_decoded_string, defects). The -# cte_decoded_string is the original binary that was encoded using the -# specified cte. 'defects' is a list of MessageDefect instances indicating any -# problems encountered during conversion. 'charset' and 'lang' are the -# corresponding strings extracted from the EW, case preserved. -# -# The general interface for a CTE encoder is that it takes a binary sequence -# as input and returns the cte_encoded_string, which is an ascii-only string. -# -# Each decoder must also supply a length function that takes the binary -# sequence as its argument and returns the length of the resulting encoded -# string. -# -# The main API functions for the module are decode, which calls the decoder -# referenced by the cte specifier, and encode, which adds the appropriate -# RFC 2047 "chrome" to the encoded string, and can optionally automatically -# select the shortest possible encoding. See their docstrings below for -# details. - -import re -import base64 -import binascii -import functools -from string import ascii_letters, digits -from future.backports.email import errors - -__all__ = ['decode_q', - 'encode_q', - 'decode_b', - 'encode_b', - 'len_q', - 'len_b', - 'decode', - 'encode', - ] - -# -# Quoted Printable -# - -# regex based decoder. -_q_byte_subber = functools.partial(re.compile(br'=([a-fA-F0-9]{2})').sub, - lambda m: bytes([int(m.group(1), 16)])) - -def decode_q(encoded): - encoded = bytes(encoded.replace(b'_', b' ')) - return _q_byte_subber(encoded), [] - - -# dict mapping bytes to their encoded form -class _QByteMap(dict): - - safe = bytes(b'-!*+/' + ascii_letters.encode('ascii') + digits.encode('ascii')) - - def __missing__(self, key): - if key in self.safe: - self[key] = chr(key) - else: - self[key] = "={:02X}".format(key) - return self[key] - -_q_byte_map = _QByteMap() - -# In headers spaces are mapped to '_'. -_q_byte_map[ord(' ')] = '_' - -def encode_q(bstring): - return str(''.join(_q_byte_map[x] for x in bytes(bstring))) - -def len_q(bstring): - return sum(len(_q_byte_map[x]) for x in bytes(bstring)) - - -# -# Base64 -# - -def decode_b(encoded): - defects = [] - pad_err = len(encoded) % 4 - if pad_err: - defects.append(errors.InvalidBase64PaddingDefect()) - padded_encoded = encoded + b'==='[:4-pad_err] - else: - padded_encoded = encoded - try: - # The validate kwarg to b64decode is not supported in Py2.x - if not re.match(b'^[A-Za-z0-9+/]*={0,2}$', padded_encoded): - raise binascii.Error('Non-base64 digit found') - return base64.b64decode(padded_encoded), defects - except binascii.Error: - # Since we had correct padding, this must an invalid char error. - defects = [errors.InvalidBase64CharactersDefect()] - # The non-alphabet characters are ignored as far as padding - # goes, but we don't know how many there are. So we'll just - # try various padding lengths until something works. - for i in 0, 1, 2, 3: - try: - return base64.b64decode(encoded+b'='*i), defects - except (binascii.Error, TypeError): # Py2 raises a TypeError - if i==0: - defects.append(errors.InvalidBase64PaddingDefect()) - else: - # This should never happen. - raise AssertionError("unexpected binascii.Error") - -def encode_b(bstring): - return base64.b64encode(bstring).decode('ascii') - -def len_b(bstring): - groups_of_3, leftover = divmod(len(bstring), 3) - # 4 bytes out for each 3 bytes (or nonzero fraction thereof) in. - return groups_of_3 * 4 + (4 if leftover else 0) - - -_cte_decoders = { - 'q': decode_q, - 'b': decode_b, - } - -def decode(ew): - """Decode encoded word and return (string, charset, lang, defects) tuple. - - An RFC 2047/2243 encoded word has the form: - - =?charset*lang?cte?encoded_string?= - - where '*lang' may be omitted but the other parts may not be. - - This function expects exactly such a string (that is, it does not check the - syntax and may raise errors if the string is not well formed), and returns - the encoded_string decoded first from its Content Transfer Encoding and - then from the resulting bytes into unicode using the specified charset. If - the cte-decoded string does not successfully decode using the specified - character set, a defect is added to the defects list and the unknown octets - are replaced by the unicode 'unknown' character \uFDFF. - - The specified charset and language are returned. The default for language, - which is rarely if ever encountered, is the empty string. - - """ - _, charset, cte, cte_string, _ = str(ew).split('?') - charset, _, lang = charset.partition('*') - cte = cte.lower() - # Recover the original bytes and do CTE decoding. - bstring = cte_string.encode('ascii', 'surrogateescape') - bstring, defects = _cte_decoders[cte](bstring) - # Turn the CTE decoded bytes into unicode. - try: - string = bstring.decode(charset) - except UnicodeError: - defects.append(errors.UndecodableBytesDefect("Encoded word " - "contains bytes not decodable using {} charset".format(charset))) - string = bstring.decode(charset, 'surrogateescape') - except LookupError: - string = bstring.decode('ascii', 'surrogateescape') - if charset.lower() != 'unknown-8bit': - defects.append(errors.CharsetError("Unknown charset {} " - "in encoded word; decoded as unknown bytes".format(charset))) - return string, charset, lang, defects - - -_cte_encoders = { - 'q': encode_q, - 'b': encode_b, - } - -_cte_encode_length = { - 'q': len_q, - 'b': len_b, - } - -def encode(string, charset='utf-8', encoding=None, lang=''): - """Encode string using the CTE encoding that produces the shorter result. - - Produces an RFC 2047/2243 encoded word of the form: - - =?charset*lang?cte?encoded_string?= - - where '*lang' is omitted unless the 'lang' parameter is given a value. - Optional argument charset (defaults to utf-8) specifies the charset to use - to encode the string to binary before CTE encoding it. Optional argument - 'encoding' is the cte specifier for the encoding that should be used ('q' - or 'b'); if it is None (the default) the encoding which produces the - shortest encoded sequence is used, except that 'q' is preferred if it is up - to five characters longer. Optional argument 'lang' (default '') gives the - RFC 2243 language string to specify in the encoded word. - - """ - string = str(string) - if charset == 'unknown-8bit': - bstring = string.encode('ascii', 'surrogateescape') - else: - bstring = string.encode(charset) - if encoding is None: - qlen = _cte_encode_length['q'](bstring) - blen = _cte_encode_length['b'](bstring) - # Bias toward q. 5 is arbitrary. - encoding = 'q' if qlen - blen < 5 else 'b' - encoded = _cte_encoders[encoding](bstring) - if lang: - lang = '*' + lang - return "=?{0}{1}?{2}?{3}?=".format(charset, lang, encoding, encoded) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/encoders.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/encoders.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/encoders.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/encoders.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Encodings and related functions.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import str - -__all__ = [ - 'encode_7or8bit', - 'encode_base64', - 'encode_noop', - 'encode_quopri', - ] - - -try: - from base64 import encodebytes as _bencode -except ImportError: - # Py2 compatibility. TODO: test this! - from base64 import encodestring as _bencode -from quopri import encodestring as _encodestring - - -def _qencode(s): - enc = _encodestring(s, quotetabs=True) - # Must encode spaces, which quopri.encodestring() doesn't do - return enc.replace(' ', '=20') - - -def encode_base64(msg): - """Encode the message's payload in Base64. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = str(_bencode(orig), 'ascii') - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'base64' - - -def encode_quopri(msg): - """Encode the message's payload in quoted-printable. - - Also, add an appropriate Content-Transfer-Encoding header. - """ - orig = msg.get_payload() - encdata = _qencode(orig) - msg.set_payload(encdata) - msg['Content-Transfer-Encoding'] = 'quoted-printable' - - -def encode_7or8bit(msg): - """Set the Content-Transfer-Encoding header to 7bit or 8bit.""" - orig = msg.get_payload() - if orig is None: - # There's no payload. For backwards compatibility we use 7bit - msg['Content-Transfer-Encoding'] = '7bit' - return - # We play a trick to make this go fast. If encoding/decode to ASCII - # succeeds, we know the data must be 7bit, otherwise treat it as 8bit. - try: - if isinstance(orig, str): - orig.encode('ascii') - else: - orig.decode('ascii') - except UnicodeError: - charset = msg.get_charset() - output_cset = charset and charset.output_charset - # iso-2022-* is non-ASCII but encodes to a 7-bit representation - if output_cset and output_cset.lower().startswith('iso-2022-'): - msg['Content-Transfer-Encoding'] = '7bit' - else: - msg['Content-Transfer-Encoding'] = '8bit' - else: - msg['Content-Transfer-Encoding'] = '7bit' - if not isinstance(orig, str): - msg.set_payload(orig.decode('ascii', 'surrogateescape')) - - -def encode_noop(msg): - """Do nothing.""" - # Well, not quite *nothing*: in Python3 we have to turn bytes into a string - # in our internal surrogateescaped form in order to keep the model - # consistent. - orig = msg.get_payload() - if not isinstance(orig, str): - msg.set_payload(orig.decode('ascii', 'surrogateescape')) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/errors.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/errors.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/errors.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/errors.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""email package exception classes.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super - - -class MessageError(Exception): - """Base class for errors in the email package.""" - - -class MessageParseError(MessageError): - """Base class for message parsing errors.""" - - -class HeaderParseError(MessageParseError): - """Error while parsing headers.""" - - -class BoundaryError(MessageParseError): - """Couldn't find terminating boundary.""" - - -class MultipartConversionError(MessageError, TypeError): - """Conversion to a multipart is prohibited.""" - - -class CharsetError(MessageError): - """An illegal charset was given.""" - - -# These are parsing defects which the parser was able to work around. -class MessageDefect(ValueError): - """Base class for a message defect.""" - - def __init__(self, line=None): - if line is not None: - super().__init__(line) - self.line = line - -class NoBoundaryInMultipartDefect(MessageDefect): - """A message claimed to be a multipart but had no boundary parameter.""" - -class StartBoundaryNotFoundDefect(MessageDefect): - """The claimed start boundary was never found.""" - -class CloseBoundaryNotFoundDefect(MessageDefect): - """A start boundary was found, but not the corresponding close boundary.""" - -class FirstHeaderLineIsContinuationDefect(MessageDefect): - """A message had a continuation line as its first header line.""" - -class MisplacedEnvelopeHeaderDefect(MessageDefect): - """A 'Unix-from' header was found in the middle of a header block.""" - -class MissingHeaderBodySeparatorDefect(MessageDefect): - """Found line with no leading whitespace and no colon before blank line.""" -# XXX: backward compatibility, just in case (it was never emitted). -MalformedHeaderDefect = MissingHeaderBodySeparatorDefect - -class MultipartInvariantViolationDefect(MessageDefect): - """A message claimed to be a multipart but no subparts were found.""" - -class InvalidMultipartContentTransferEncodingDefect(MessageDefect): - """An invalid content transfer encoding was set on the multipart itself.""" - -class UndecodableBytesDefect(MessageDefect): - """Header contained bytes that could not be decoded""" - -class InvalidBase64PaddingDefect(MessageDefect): - """base64 encoded sequence had an incorrect length""" - -class InvalidBase64CharactersDefect(MessageDefect): - """base64 encoded sequence had characters not in base64 alphabet""" - -# These errors are specific to header parsing. - -class HeaderDefect(MessageDefect): - """Base class for a header defect.""" - - def __init__(self, *args, **kw): - super().__init__(*args, **kw) - -class InvalidHeaderDefect(HeaderDefect): - """Header is not valid, message gives details.""" - -class HeaderMissingRequiredValue(HeaderDefect): - """A header that must have a value had none""" - -class NonPrintableDefect(HeaderDefect): - """ASCII characters outside the ascii-printable range found""" - - def __init__(self, non_printables): - super().__init__(non_printables) - self.non_printables = non_printables - - def __str__(self): - return ("the following ASCII non-printables found in header: " - "{}".format(self.non_printables)) - -class ObsoleteHeaderDefect(HeaderDefect): - """Header uses syntax declared obsolete by RFC 5322""" - -class NonASCIILocalPartDefect(HeaderDefect): - """local_part contains non-ASCII characters""" - # This defect only occurs during unicode parsing, not when - # parsing messages decoded from binary. diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/feedparser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/feedparser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/feedparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/feedparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,525 +0,0 @@ -# Copyright (C) 2004-2006 Python Software Foundation -# Authors: Baxter, Wouters and Warsaw -# Contact: email-sig@python.org - -"""FeedParser - An email feed parser. - -The feed parser implements an interface for incrementally parsing an email -message, line by line. This has advantages for certain applications, such as -those reading email messages off a socket. - -FeedParser.feed() is the primary interface for pushing new data into the -parser. It returns when there's nothing more it can do with the available -data. When you have no more data to push into the parser, call .close(). -This completes the parsing and returns the root message object. - -The other advantage of this parser is that it will never raise a parsing -exception. Instead, when it finds something unexpected, it adds a 'defect' to -the current message. Defects are just instances that live on the message -object's .defects attribute. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import object, range, super -from future.utils import implements_iterator, PY3 - -__all__ = ['FeedParser', 'BytesFeedParser'] - -import re - -from future.backports.email import errors -from future.backports.email import message -from future.backports.email._policybase import compat32 - -NLCRE = re.compile('\r\n|\r|\n') -NLCRE_bol = re.compile('(\r\n|\r|\n)') -NLCRE_eol = re.compile('(\r\n|\r|\n)\Z') -NLCRE_crack = re.compile('(\r\n|\r|\n)') -# RFC 2822 $3.6.8 Optional fields. ftext is %d33-57 / %d59-126, Any character -# except controls, SP, and ":". -headerRE = re.compile(r'^(From |[\041-\071\073-\176]{1,}:|[\t ])') -EMPTYSTRING = '' -NL = '\n' - -NeedMoreData = object() - - -# @implements_iterator -class BufferedSubFile(object): - """A file-ish object that can have new data loaded into it. - - You can also push and pop line-matching predicates onto a stack. When the - current predicate matches the current line, a false EOF response - (i.e. empty string) is returned instead. This lets the parser adhere to a - simple abstraction -- it parses until EOF closes the current message. - """ - def __init__(self): - # The last partial line pushed into this object. - self._partial = '' - # The list of full, pushed lines, in reverse order - self._lines = [] - # The stack of false-EOF checking predicates. - self._eofstack = [] - # A flag indicating whether the file has been closed or not. - self._closed = False - - def push_eof_matcher(self, pred): - self._eofstack.append(pred) - - def pop_eof_matcher(self): - return self._eofstack.pop() - - def close(self): - # Don't forget any trailing partial line. - self._lines.append(self._partial) - self._partial = '' - self._closed = True - - def readline(self): - if not self._lines: - if self._closed: - return '' - return NeedMoreData - # Pop the line off the stack and see if it matches the current - # false-EOF predicate. - line = self._lines.pop() - # RFC 2046, section 5.1.2 requires us to recognize outer level - # boundaries at any level of inner nesting. Do this, but be sure it's - # in the order of most to least nested. - for ateof in self._eofstack[::-1]: - if ateof(line): - # We're at the false EOF. But push the last line back first. - self._lines.append(line) - return '' - return line - - def unreadline(self, line): - # Let the consumer push a line back into the buffer. - assert line is not NeedMoreData - self._lines.append(line) - - def push(self, data): - """Push some new data into this object.""" - # Handle any previous leftovers - data, self._partial = self._partial + data, '' - # Crack into lines, but preserve the newlines on the end of each - parts = NLCRE_crack.split(data) - # The *ahem* interesting behaviour of re.split when supplied grouping - # parentheses is that the last element of the resulting list is the - # data after the final RE. In the case of a NL/CR terminated string, - # this is the empty string. - self._partial = parts.pop() - #GAN 29Mar09 bugs 1555570, 1721862 Confusion at 8K boundary ending with \r: - # is there a \n to follow later? - if not self._partial and parts and parts[-1].endswith('\r'): - self._partial = parts.pop(-2)+parts.pop() - # parts is a list of strings, alternating between the line contents - # and the eol character(s). Gather up a list of lines after - # re-attaching the newlines. - lines = [] - for i in range(len(parts) // 2): - lines.append(parts[i*2] + parts[i*2+1]) - self.pushlines(lines) - - def pushlines(self, lines): - # Reverse and insert at the front of the lines. - self._lines[:0] = lines[::-1] - - def __iter__(self): - return self - - def __next__(self): - line = self.readline() - if line == '': - raise StopIteration - return line - - -class FeedParser(object): - """A feed-style parser of email.""" - - def __init__(self, _factory=message.Message, **_3to2kwargs): - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = compat32 - """_factory is called with no arguments to create a new message obj - - The policy keyword specifies a policy object that controls a number of - aspects of the parser's operation. The default policy maintains - backward compatibility. - - """ - self._factory = _factory - self.policy = policy - try: - _factory(policy=self.policy) - self._factory_kwds = lambda: {'policy': self.policy} - except TypeError: - # Assume this is an old-style factory - self._factory_kwds = lambda: {} - self._input = BufferedSubFile() - self._msgstack = [] - if PY3: - self._parse = self._parsegen().__next__ - else: - self._parse = self._parsegen().next - self._cur = None - self._last = None - self._headersonly = False - - # Non-public interface for supporting Parser's headersonly flag - def _set_headersonly(self): - self._headersonly = True - - def feed(self, data): - """Push more data into the parser.""" - self._input.push(data) - self._call_parse() - - def _call_parse(self): - try: - self._parse() - except StopIteration: - pass - - def close(self): - """Parse all remaining data and return the root message object.""" - self._input.close() - self._call_parse() - root = self._pop_message() - assert not self._msgstack - # Look for final set of defects - if root.get_content_maintype() == 'multipart' \ - and not root.is_multipart(): - defect = errors.MultipartInvariantViolationDefect() - self.policy.handle_defect(root, defect) - return root - - def _new_message(self): - msg = self._factory(**self._factory_kwds()) - if self._cur and self._cur.get_content_type() == 'multipart/digest': - msg.set_default_type('message/rfc822') - if self._msgstack: - self._msgstack[-1].attach(msg) - self._msgstack.append(msg) - self._cur = msg - self._last = msg - - def _pop_message(self): - retval = self._msgstack.pop() - if self._msgstack: - self._cur = self._msgstack[-1] - else: - self._cur = None - return retval - - def _parsegen(self): - # Create a new message and start by parsing headers. - self._new_message() - headers = [] - # Collect the headers, searching for a line that doesn't match the RFC - # 2822 header or continuation pattern (including an empty line). - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - if not headerRE.match(line): - # If we saw the RFC defined header/body separator - # (i.e. newline), just throw it away. Otherwise the line is - # part of the body so push it back. - if not NLCRE.match(line): - defect = errors.MissingHeaderBodySeparatorDefect() - self.policy.handle_defect(self._cur, defect) - self._input.unreadline(line) - break - headers.append(line) - # Done with the headers, so parse them and figure out what we're - # supposed to see in the body of the message. - self._parse_headers(headers) - # Headers-only parsing is a backwards compatibility hack, which was - # necessary in the older parser, which could raise errors. All - # remaining lines in the input are thrown into the message body. - if self._headersonly: - lines = [] - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - if self._cur.get_content_type() == 'message/delivery-status': - # message/delivery-status contains blocks of headers separated by - # a blank line. We'll represent each header block as a separate - # nested message object, but the processing is a bit different - # than standard message/* types because there is no body for the - # nested messages. A blank line separates the subparts. - while True: - self._input.push_eof_matcher(NLCRE.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - msg = self._pop_message() - # We need to pop the EOF matcher in order to tell if we're at - # the end of the current file, not the end of the last block - # of message headers. - self._input.pop_eof_matcher() - # The input stream must be sitting at the newline or at the - # EOF. We want to see if we're at the end of this subpart, so - # first consume the blank line, then test the next line to see - # if we're at this subpart's EOF. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - break - if line == '': - break - # Not at EOF so this is a line we're going to need. - self._input.unreadline(line) - return - if self._cur.get_content_maintype() == 'message': - # The message claims to be a message/* type, then what follows is - # another RFC 2822 message. - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - self._pop_message() - return - if self._cur.get_content_maintype() == 'multipart': - boundary = self._cur.get_boundary() - if boundary is None: - # The message /claims/ to be a multipart but it has not - # defined a boundary. That's a problem which we'll handle by - # reading everything until the EOF and marking the message as - # defective. - defect = errors.NoBoundaryInMultipartDefect() - self.policy.handle_defect(self._cur, defect) - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - return - # Make sure a valid content type was specified per RFC 2045:6.4. - if (self._cur.get('content-transfer-encoding', '8bit').lower() - not in ('7bit', '8bit', 'binary')): - defect = errors.InvalidMultipartContentTransferEncodingDefect() - self.policy.handle_defect(self._cur, defect) - # Create a line match predicate which matches the inter-part - # boundary as well as the end-of-multipart boundary. Don't push - # this onto the input stream until we've scanned past the - # preamble. - separator = '--' + boundary - boundaryre = re.compile( - '(?P' + re.escape(separator) + - r')(?P--)?(?P[ \t]*)(?P\r\n|\r|\n)?$') - capturing_preamble = True - preamble = [] - linesep = False - close_boundary_seen = False - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - if line == '': - break - mo = boundaryre.match(line) - if mo: - # If we're looking at the end boundary, we're done with - # this multipart. If there was a newline at the end of - # the closing boundary, then we need to initialize the - # epilogue with the empty string (see below). - if mo.group('end'): - close_boundary_seen = True - linesep = mo.group('linesep') - break - # We saw an inter-part boundary. Were we in the preamble? - if capturing_preamble: - if preamble: - # According to RFC 2046, the last newline belongs - # to the boundary. - lastline = preamble[-1] - eolmo = NLCRE_eol.search(lastline) - if eolmo: - preamble[-1] = lastline[:-len(eolmo.group(0))] - self._cur.preamble = EMPTYSTRING.join(preamble) - capturing_preamble = False - self._input.unreadline(line) - continue - # We saw a boundary separating two parts. Consume any - # multiple boundary lines that may be following. Our - # interpretation of RFC 2046 BNF grammar does not produce - # body parts within such double boundaries. - while True: - line = self._input.readline() - if line is NeedMoreData: - yield NeedMoreData - continue - mo = boundaryre.match(line) - if not mo: - self._input.unreadline(line) - break - # Recurse to parse this subpart; the input stream points - # at the subpart's first line. - self._input.push_eof_matcher(boundaryre.match) - for retval in self._parsegen(): - if retval is NeedMoreData: - yield NeedMoreData - continue - break - # Because of RFC 2046, the newline preceding the boundary - # separator actually belongs to the boundary, not the - # previous subpart's payload (or epilogue if the previous - # part is a multipart). - if self._last.get_content_maintype() == 'multipart': - epilogue = self._last.epilogue - if epilogue == '': - self._last.epilogue = None - elif epilogue is not None: - mo = NLCRE_eol.search(epilogue) - if mo: - end = len(mo.group(0)) - self._last.epilogue = epilogue[:-end] - else: - payload = self._last._payload - if isinstance(payload, str): - mo = NLCRE_eol.search(payload) - if mo: - payload = payload[:-len(mo.group(0))] - self._last._payload = payload - self._input.pop_eof_matcher() - self._pop_message() - # Set the multipart up for newline cleansing, which will - # happen if we're in a nested multipart. - self._last = self._cur - else: - # I think we must be in the preamble - assert capturing_preamble - preamble.append(line) - # We've seen either the EOF or the end boundary. If we're still - # capturing the preamble, we never saw the start boundary. Note - # that as a defect and store the captured text as the payload. - if capturing_preamble: - defect = errors.StartBoundaryNotFoundDefect() - self.policy.handle_defect(self._cur, defect) - self._cur.set_payload(EMPTYSTRING.join(preamble)) - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # If we're not processing the preamble, then we might have seen - # EOF without seeing that end boundary...that is also a defect. - if not close_boundary_seen: - defect = errors.CloseBoundaryNotFoundDefect() - self.policy.handle_defect(self._cur, defect) - return - # Everything from here to the EOF is epilogue. If the end boundary - # ended in a newline, we'll need to make sure the epilogue isn't - # None - if linesep: - epilogue = [''] - else: - epilogue = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - epilogue.append(line) - # Any CRLF at the front of the epilogue is not technically part of - # the epilogue. Also, watch out for an empty string epilogue, - # which means a single newline. - if epilogue: - firstline = epilogue[0] - bolmo = NLCRE_bol.match(firstline) - if bolmo: - epilogue[0] = firstline[len(bolmo.group(0)):] - self._cur.epilogue = EMPTYSTRING.join(epilogue) - return - # Otherwise, it's some non-multipart type, so the entire rest of the - # file contents becomes the payload. - lines = [] - for line in self._input: - if line is NeedMoreData: - yield NeedMoreData - continue - lines.append(line) - self._cur.set_payload(EMPTYSTRING.join(lines)) - - def _parse_headers(self, lines): - # Passed a list of lines that make up the headers for the current msg - lastheader = '' - lastvalue = [] - for lineno, line in enumerate(lines): - # Check for continuation - if line[0] in ' \t': - if not lastheader: - # The first line of the headers was a continuation. This - # is illegal, so let's note the defect, store the illegal - # line, and ignore it for purposes of headers. - defect = errors.FirstHeaderLineIsContinuationDefect(line) - self.policy.handle_defect(self._cur, defect) - continue - lastvalue.append(line) - continue - if lastheader: - self._cur.set_raw(*self.policy.header_source_parse(lastvalue)) - lastheader, lastvalue = '', [] - # Check for envelope header, i.e. unix-from - if line.startswith('From '): - if lineno == 0: - # Strip off the trailing newline - mo = NLCRE_eol.search(line) - if mo: - line = line[:-len(mo.group(0))] - self._cur.set_unixfrom(line) - continue - elif lineno == len(lines) - 1: - # Something looking like a unix-from at the end - it's - # probably the first line of the body, so push back the - # line and stop. - self._input.unreadline(line) - return - else: - # Weirdly placed unix-from line. Note this as a defect - # and ignore it. - defect = errors.MisplacedEnvelopeHeaderDefect(line) - self._cur.defects.append(defect) - continue - # Split the line on the colon separating field name from value. - # There will always be a colon, because if there wasn't the part of - # the parser that calls us would have started parsing the body. - i = line.find(':') - assert i>0, "_parse_headers fed line with no : and no leading WS" - lastheader = line[:i] - lastvalue = [line] - # Done with all the lines, so handle the last header. - if lastheader: - self._cur.set_raw(*self.policy.header_source_parse(lastvalue)) - - -class BytesFeedParser(FeedParser): - """Like FeedParser, but feed accepts bytes.""" - - def feed(self, data): - super().feed(data.decode('ascii', 'surrogateescape')) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/generator.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/generator.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/generator.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/generator.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,498 +0,0 @@ -# Copyright (C) 2001-2010 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Classes to generate plain text from a message object tree.""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super -from future.builtins import str - -__all__ = ['Generator', 'DecodedGenerator', 'BytesGenerator'] - -import re -import sys -import time -import random -import warnings - -from io import StringIO, BytesIO -from future.backports.email._policybase import compat32 -from future.backports.email.header import Header -from future.backports.email.utils import _has_surrogates -import future.backports.email.charset as _charset - -UNDERSCORE = '_' -NL = '\n' # XXX: no longer used by the code below. - -fcre = re.compile(r'^From ', re.MULTILINE) - - -class Generator(object): - """Generates output from a Message object tree. - - This basic generator writes the message to the given file object as plain - text. - """ - # - # Public interface - # - - def __init__(self, outfp, mangle_from_=True, maxheaderlen=None, **_3to2kwargs): - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = None - """Create the generator for message flattening. - - outfp is the output file-like object for writing the message to. It - must have a write() method. - - Optional mangle_from_ is a flag that, when True (the default), escapes - From_ lines in the body of the message by putting a `>' in front of - them. - - Optional maxheaderlen specifies the longest length for a non-continued - header. When a header line is longer (in characters, with tabs - expanded to 8 spaces) than maxheaderlen, the header will split as - defined in the Header class. Set maxheaderlen to zero to disable - header wrapping. The default is 78, as recommended (but not required) - by RFC 2822. - - The policy keyword specifies a policy object that controls a number of - aspects of the generator's operation. The default policy maintains - backward compatibility. - - """ - self._fp = outfp - self._mangle_from_ = mangle_from_ - self.maxheaderlen = maxheaderlen - self.policy = policy - - def write(self, s): - # Just delegate to the file object - self._fp.write(s) - - def flatten(self, msg, unixfrom=False, linesep=None): - r"""Print the message object tree rooted at msg to the output file - specified when the Generator instance was created. - - unixfrom is a flag that forces the printing of a Unix From_ delimiter - before the first object in the message tree. If the original message - has no From_ delimiter, a `standard' one is crafted. By default, this - is False to inhibit the printing of any From_ delimiter. - - Note that for subobjects, no From_ line is printed. - - linesep specifies the characters used to indicate a new line in - the output. The default value is determined by the policy. - - """ - # We use the _XXX constants for operating on data that comes directly - # from the msg, and _encoded_XXX constants for operating on data that - # has already been converted (to bytes in the BytesGenerator) and - # inserted into a temporary buffer. - policy = msg.policy if self.policy is None else self.policy - if linesep is not None: - policy = policy.clone(linesep=linesep) - if self.maxheaderlen is not None: - policy = policy.clone(max_line_length=self.maxheaderlen) - self._NL = policy.linesep - self._encoded_NL = self._encode(self._NL) - self._EMPTY = '' - self._encoded_EMTPY = self._encode('') - # Because we use clone (below) when we recursively process message - # subparts, and because clone uses the computed policy (not None), - # submessages will automatically get set to the computed policy when - # they are processed by this code. - old_gen_policy = self.policy - old_msg_policy = msg.policy - try: - self.policy = policy - msg.policy = policy - if unixfrom: - ufrom = msg.get_unixfrom() - if not ufrom: - ufrom = 'From nobody ' + time.ctime(time.time()) - self.write(ufrom + self._NL) - self._write(msg) - finally: - self.policy = old_gen_policy - msg.policy = old_msg_policy - - def clone(self, fp): - """Clone this generator with the exact same options.""" - return self.__class__(fp, - self._mangle_from_, - None, # Use policy setting, which we've adjusted - policy=self.policy) - - # - # Protected interface - undocumented ;/ - # - - # Note that we use 'self.write' when what we are writing is coming from - # the source, and self._fp.write when what we are writing is coming from a - # buffer (because the Bytes subclass has already had a chance to transform - # the data in its write method in that case). This is an entirely - # pragmatic split determined by experiment; we could be more general by - # always using write and having the Bytes subclass write method detect when - # it has already transformed the input; but, since this whole thing is a - # hack anyway this seems good enough. - - # Similarly, we have _XXX and _encoded_XXX attributes that are used on - # source and buffer data, respectively. - _encoded_EMPTY = '' - - def _new_buffer(self): - # BytesGenerator overrides this to return BytesIO. - return StringIO() - - def _encode(self, s): - # BytesGenerator overrides this to encode strings to bytes. - return s - - def _write_lines(self, lines): - # We have to transform the line endings. - if not lines: - return - lines = lines.splitlines(True) - for line in lines[:-1]: - self.write(line.rstrip('\r\n')) - self.write(self._NL) - laststripped = lines[-1].rstrip('\r\n') - self.write(laststripped) - if len(lines[-1]) != len(laststripped): - self.write(self._NL) - - def _write(self, msg): - # We can't write the headers yet because of the following scenario: - # say a multipart message includes the boundary string somewhere in - # its body. We'd have to calculate the new boundary /before/ we write - # the headers so that we can write the correct Content-Type: - # parameter. - # - # The way we do this, so as to make the _handle_*() methods simpler, - # is to cache any subpart writes into a buffer. The we write the - # headers and the buffer contents. That way, subpart handlers can - # Do The Right Thing, and can still modify the Content-Type: header if - # necessary. - oldfp = self._fp - try: - self._fp = sfp = self._new_buffer() - self._dispatch(msg) - finally: - self._fp = oldfp - # Write the headers. First we see if the message object wants to - # handle that itself. If not, we'll do it generically. - meth = getattr(msg, '_write_headers', None) - if meth is None: - self._write_headers(msg) - else: - meth(self) - self._fp.write(sfp.getvalue()) - - def _dispatch(self, msg): - # Get the Content-Type: for the message, then try to dispatch to - # self._handle__(). If there's no handler for the - # full MIME type, then dispatch to self._handle_(). If - # that's missing too, then dispatch to self._writeBody(). - main = msg.get_content_maintype() - sub = msg.get_content_subtype() - specific = UNDERSCORE.join((main, sub)).replace('-', '_') - meth = getattr(self, '_handle_' + specific, None) - if meth is None: - generic = main.replace('-', '_') - meth = getattr(self, '_handle_' + generic, None) - if meth is None: - meth = self._writeBody - meth(msg) - - # - # Default handlers - # - - def _write_headers(self, msg): - for h, v in msg.raw_items(): - self.write(self.policy.fold(h, v)) - # A blank line always separates headers from body - self.write(self._NL) - - # - # Handlers for writing types and subtypes - # - - def _handle_text(self, msg): - payload = msg.get_payload() - if payload is None: - return - if not isinstance(payload, str): - raise TypeError('string payload expected: %s' % type(payload)) - if _has_surrogates(msg._payload): - charset = msg.get_param('charset') - if charset is not None: - del msg['content-transfer-encoding'] - msg.set_payload(payload, charset) - payload = msg.get_payload() - if self._mangle_from_: - payload = fcre.sub('>From ', payload) - self._write_lines(payload) - - # Default body handler - _writeBody = _handle_text - - def _handle_multipart(self, msg): - # The trick here is to write out each part separately, merge them all - # together, and then make sure that the boundary we've chosen isn't - # present in the payload. - msgtexts = [] - subparts = msg.get_payload() - if subparts is None: - subparts = [] - elif isinstance(subparts, str): - # e.g. a non-strict parse of a message with no starting boundary. - self.write(subparts) - return - elif not isinstance(subparts, list): - # Scalar payload - subparts = [subparts] - for part in subparts: - s = self._new_buffer() - g = self.clone(s) - g.flatten(part, unixfrom=False, linesep=self._NL) - msgtexts.append(s.getvalue()) - # BAW: What about boundaries that are wrapped in double-quotes? - boundary = msg.get_boundary() - if not boundary: - # Create a boundary that doesn't appear in any of the - # message texts. - alltext = self._encoded_NL.join(msgtexts) - boundary = self._make_boundary(alltext) - msg.set_boundary(boundary) - # If there's a preamble, write it out, with a trailing CRLF - if msg.preamble is not None: - if self._mangle_from_: - preamble = fcre.sub('>From ', msg.preamble) - else: - preamble = msg.preamble - self._write_lines(preamble) - self.write(self._NL) - # dash-boundary transport-padding CRLF - self.write('--' + boundary + self._NL) - # body-part - if msgtexts: - self._fp.write(msgtexts.pop(0)) - # *encapsulation - # --> delimiter transport-padding - # --> CRLF body-part - for body_part in msgtexts: - # delimiter transport-padding CRLF - self.write(self._NL + '--' + boundary + self._NL) - # body-part - self._fp.write(body_part) - # close-delimiter transport-padding - self.write(self._NL + '--' + boundary + '--') - if msg.epilogue is not None: - self.write(self._NL) - if self._mangle_from_: - epilogue = fcre.sub('>From ', msg.epilogue) - else: - epilogue = msg.epilogue - self._write_lines(epilogue) - - def _handle_multipart_signed(self, msg): - # The contents of signed parts has to stay unmodified in order to keep - # the signature intact per RFC1847 2.1, so we disable header wrapping. - # RDM: This isn't enough to completely preserve the part, but it helps. - p = self.policy - self.policy = p.clone(max_line_length=0) - try: - self._handle_multipart(msg) - finally: - self.policy = p - - def _handle_message_delivery_status(self, msg): - # We can't just write the headers directly to self's file object - # because this will leave an extra newline between the last header - # block and the boundary. Sigh. - blocks = [] - for part in msg.get_payload(): - s = self._new_buffer() - g = self.clone(s) - g.flatten(part, unixfrom=False, linesep=self._NL) - text = s.getvalue() - lines = text.split(self._encoded_NL) - # Strip off the unnecessary trailing empty line - if lines and lines[-1] == self._encoded_EMPTY: - blocks.append(self._encoded_NL.join(lines[:-1])) - else: - blocks.append(text) - # Now join all the blocks with an empty line. This has the lovely - # effect of separating each block with an empty line, but not adding - # an extra one after the last one. - self._fp.write(self._encoded_NL.join(blocks)) - - def _handle_message(self, msg): - s = self._new_buffer() - g = self.clone(s) - # The payload of a message/rfc822 part should be a multipart sequence - # of length 1. The zeroth element of the list should be the Message - # object for the subpart. Extract that object, stringify it, and - # write it out. - # Except, it turns out, when it's a string instead, which happens when - # and only when HeaderParser is used on a message of mime type - # message/rfc822. Such messages are generated by, for example, - # Groupwise when forwarding unadorned messages. (Issue 7970.) So - # in that case we just emit the string body. - payload = msg._payload - if isinstance(payload, list): - g.flatten(msg.get_payload(0), unixfrom=False, linesep=self._NL) - payload = s.getvalue() - else: - payload = self._encode(payload) - self._fp.write(payload) - - # This used to be a module level function; we use a classmethod for this - # and _compile_re so we can continue to provide the module level function - # for backward compatibility by doing - # _make_boudary = Generator._make_boundary - # at the end of the module. It *is* internal, so we could drop that... - @classmethod - def _make_boundary(cls, text=None): - # Craft a random boundary. If text is given, ensure that the chosen - # boundary doesn't appear in the text. - token = random.randrange(sys.maxsize) - boundary = ('=' * 15) + (_fmt % token) + '==' - if text is None: - return boundary - b = boundary - counter = 0 - while True: - cre = cls._compile_re('^--' + re.escape(b) + '(--)?$', re.MULTILINE) - if not cre.search(text): - break - b = boundary + '.' + str(counter) - counter += 1 - return b - - @classmethod - def _compile_re(cls, s, flags): - return re.compile(s, flags) - -class BytesGenerator(Generator): - """Generates a bytes version of a Message object tree. - - Functionally identical to the base Generator except that the output is - bytes and not string. When surrogates were used in the input to encode - bytes, these are decoded back to bytes for output. If the policy has - cte_type set to 7bit, then the message is transformed such that the - non-ASCII bytes are properly content transfer encoded, using the charset - unknown-8bit. - - The outfp object must accept bytes in its write method. - """ - - # Bytes versions of this constant for use in manipulating data from - # the BytesIO buffer. - _encoded_EMPTY = b'' - - def write(self, s): - self._fp.write(str(s).encode('ascii', 'surrogateescape')) - - def _new_buffer(self): - return BytesIO() - - def _encode(self, s): - return s.encode('ascii') - - def _write_headers(self, msg): - # This is almost the same as the string version, except for handling - # strings with 8bit bytes. - for h, v in msg.raw_items(): - self._fp.write(self.policy.fold_binary(h, v)) - # A blank line always separates headers from body - self.write(self._NL) - - def _handle_text(self, msg): - # If the string has surrogates the original source was bytes, so - # just write it back out. - if msg._payload is None: - return - if _has_surrogates(msg._payload) and not self.policy.cte_type=='7bit': - if self._mangle_from_: - msg._payload = fcre.sub(">From ", msg._payload) - self._write_lines(msg._payload) - else: - super(BytesGenerator,self)._handle_text(msg) - - # Default body handler - _writeBody = _handle_text - - @classmethod - def _compile_re(cls, s, flags): - return re.compile(s.encode('ascii'), flags) - - -_FMT = '[Non-text (%(type)s) part of message omitted, filename %(filename)s]' - -class DecodedGenerator(Generator): - """Generates a text representation of a message. - - Like the Generator base class, except that non-text parts are substituted - with a format string representing the part. - """ - def __init__(self, outfp, mangle_from_=True, maxheaderlen=78, fmt=None): - """Like Generator.__init__() except that an additional optional - argument is allowed. - - Walks through all subparts of a message. If the subpart is of main - type `text', then it prints the decoded payload of the subpart. - - Otherwise, fmt is a format string that is used instead of the message - payload. fmt is expanded with the following keywords (in - %(keyword)s format): - - type : Full MIME type of the non-text part - maintype : Main MIME type of the non-text part - subtype : Sub-MIME type of the non-text part - filename : Filename of the non-text part - description: Description associated with the non-text part - encoding : Content transfer encoding of the non-text part - - The default value for fmt is None, meaning - - [Non-text (%(type)s) part of message omitted, filename %(filename)s] - """ - Generator.__init__(self, outfp, mangle_from_, maxheaderlen) - if fmt is None: - self._fmt = _FMT - else: - self._fmt = fmt - - def _dispatch(self, msg): - for part in msg.walk(): - maintype = part.get_content_maintype() - if maintype == 'text': - print(part.get_payload(decode=False), file=self) - elif maintype == 'multipart': - # Just skip this - pass - else: - print(self._fmt % { - 'type' : part.get_content_type(), - 'maintype' : part.get_content_maintype(), - 'subtype' : part.get_content_subtype(), - 'filename' : part.get_filename('[no filename]'), - 'description': part.get('Content-Description', - '[no description]'), - 'encoding' : part.get('Content-Transfer-Encoding', - '[no encoding]'), - }, file=self) - - -# Helper used by Generator._make_boundary -_width = len(repr(sys.maxsize-1)) -_fmt = '%%0%dd' % _width - -# Backward compatibility -_make_boundary = Generator._make_boundary diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/header.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/header.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/header.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/header.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,581 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Author: Ben Gertzfield, Barry Warsaw -# Contact: email-sig@python.org - -"""Header encoding and decoding functionality.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes, range, str, super, zip - -__all__ = [ - 'Header', - 'decode_header', - 'make_header', - ] - -import re -import binascii - -from future.backports import email -from future.backports.email import base64mime -from future.backports.email.errors import HeaderParseError -import future.backports.email.charset as _charset - -# Helpers -from future.backports.email.quoprimime import _max_append, header_decode - -Charset = _charset.Charset - -NL = '\n' -SPACE = ' ' -BSPACE = b' ' -SPACE8 = ' ' * 8 -EMPTYSTRING = '' -MAXLINELEN = 78 -FWS = ' \t' - -USASCII = Charset('us-ascii') -UTF8 = Charset('utf-8') - -# Match encoded-word strings in the form =?charset?q?Hello_World?= -ecre = re.compile(r''' - =\? # literal =? - (?P[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P.*?) # non-greedy up to the next ?= is the encoded string - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE | re.MULTILINE) - -# Field name regexp, including trailing colon, but not separating whitespace, -# according to RFC 2822. Character range is from tilde to exclamation mark. -# For use with .match() -fcre = re.compile(r'[\041-\176]+:$') - -# Find a header embedded in a putative header value. Used to check for -# header injection attack. -_embeded_header = re.compile(r'\n[^ \t]+:') - - -def decode_header(header): - """Decode a message header value without converting charset. - - Returns a list of (string, charset) pairs containing each of the decoded - parts of the header. Charset is None for non-encoded parts of the header, - otherwise a lower-case string containing the name of the character set - specified in the encoded string. - - header may be a string that may or may not contain RFC2047 encoded words, - or it may be a Header object. - - An email.errors.HeaderParseError may be raised when certain decoding error - occurs (e.g. a base64 decoding exception). - """ - # If it is a Header object, we can just return the encoded chunks. - if hasattr(header, '_chunks'): - return [(_charset._encode(string, str(charset)), str(charset)) - for string, charset in header._chunks] - # If no encoding, just return the header with no charset. - if not ecre.search(header): - return [(header, None)] - # First step is to parse all the encoded parts into triplets of the form - # (encoded_string, encoding, charset). For unencoded strings, the last - # two parts will be None. - words = [] - for line in header.splitlines(): - parts = ecre.split(line) - first = True - while parts: - unencoded = parts.pop(0) - if first: - unencoded = unencoded.lstrip() - first = False - if unencoded: - words.append((unencoded, None, None)) - if parts: - charset = parts.pop(0).lower() - encoding = parts.pop(0).lower() - encoded = parts.pop(0) - words.append((encoded, encoding, charset)) - # Now loop over words and remove words that consist of whitespace - # between two encoded strings. - import sys - droplist = [] - for n, w in enumerate(words): - if n>1 and w[1] and words[n-2][1] and words[n-1][0].isspace(): - droplist.append(n-1) - for d in reversed(droplist): - del words[d] - - # The next step is to decode each encoded word by applying the reverse - # base64 or quopri transformation. decoded_words is now a list of the - # form (decoded_word, charset). - decoded_words = [] - for encoded_string, encoding, charset in words: - if encoding is None: - # This is an unencoded word. - decoded_words.append((encoded_string, charset)) - elif encoding == 'q': - word = header_decode(encoded_string) - decoded_words.append((word, charset)) - elif encoding == 'b': - paderr = len(encoded_string) % 4 # Postel's law: add missing padding - if paderr: - encoded_string += '==='[:4 - paderr] - try: - word = base64mime.decode(encoded_string) - except binascii.Error: - raise HeaderParseError('Base64 decoding error') - else: - decoded_words.append((word, charset)) - else: - raise AssertionError('Unexpected encoding: ' + encoding) - # Now convert all words to bytes and collapse consecutive runs of - # similarly encoded words. - collapsed = [] - last_word = last_charset = None - for word, charset in decoded_words: - if isinstance(word, str): - word = bytes(word, 'raw-unicode-escape') - if last_word is None: - last_word = word - last_charset = charset - elif charset != last_charset: - collapsed.append((last_word, last_charset)) - last_word = word - last_charset = charset - elif last_charset is None: - last_word += BSPACE + word - else: - last_word += word - collapsed.append((last_word, last_charset)) - return collapsed - - -def make_header(decoded_seq, maxlinelen=None, header_name=None, - continuation_ws=' '): - """Create a Header from a sequence of pairs as returned by decode_header() - - decode_header() takes a header value string and returns a sequence of - pairs of the format (decoded_string, charset) where charset is the string - name of the character set. - - This function takes one of those sequence of pairs and returns a Header - instance. Optional maxlinelen, header_name, and continuation_ws are as in - the Header constructor. - """ - h = Header(maxlinelen=maxlinelen, header_name=header_name, - continuation_ws=continuation_ws) - for s, charset in decoded_seq: - # None means us-ascii but we can simply pass it on to h.append() - if charset is not None and not isinstance(charset, Charset): - charset = Charset(charset) - h.append(s, charset) - return h - - -class Header(object): - def __init__(self, s=None, charset=None, - maxlinelen=None, header_name=None, - continuation_ws=' ', errors='strict'): - """Create a MIME-compliant header that can contain many character sets. - - Optional s is the initial header value. If None, the initial header - value is not set. You can later append to the header with .append() - method calls. s may be a byte string or a Unicode string, but see the - .append() documentation for semantics. - - Optional charset serves two purposes: it has the same meaning as the - charset argument to the .append() method. It also sets the default - character set for all subsequent .append() calls that omit the charset - argument. If charset is not provided in the constructor, the us-ascii - charset is used both as s's initial charset and as the default for - subsequent .append() calls. - - The maximum line length can be specified explicitly via maxlinelen. For - splitting the first line to a shorter value (to account for the field - header which isn't included in s, e.g. `Subject') pass in the name of - the field in header_name. The default maxlinelen is 78 as recommended - by RFC 2822. - - continuation_ws must be RFC 2822 compliant folding whitespace (usually - either a space or a hard tab) which will be prepended to continuation - lines. - - errors is passed through to the .append() call. - """ - if charset is None: - charset = USASCII - elif not isinstance(charset, Charset): - charset = Charset(charset) - self._charset = charset - self._continuation_ws = continuation_ws - self._chunks = [] - if s is not None: - self.append(s, charset, errors) - if maxlinelen is None: - maxlinelen = MAXLINELEN - self._maxlinelen = maxlinelen - if header_name is None: - self._headerlen = 0 - else: - # Take the separating colon and space into account. - self._headerlen = len(header_name) + 2 - - def __str__(self): - """Return the string value of the header.""" - self._normalize() - uchunks = [] - lastcs = None - lastspace = None - for string, charset in self._chunks: - # We must preserve spaces between encoded and non-encoded word - # boundaries, which means for us we need to add a space when we go - # from a charset to None/us-ascii, or from None/us-ascii to a - # charset. Only do this for the second and subsequent chunks. - # Don't add a space if the None/us-ascii string already has - # a space (trailing or leading depending on transition) - nextcs = charset - if nextcs == _charset.UNKNOWN8BIT: - original_bytes = string.encode('ascii', 'surrogateescape') - string = original_bytes.decode('ascii', 'replace') - if uchunks: - hasspace = string and self._nonctext(string[0]) - if lastcs not in (None, 'us-ascii'): - if nextcs in (None, 'us-ascii') and not hasspace: - uchunks.append(SPACE) - nextcs = None - elif nextcs not in (None, 'us-ascii') and not lastspace: - uchunks.append(SPACE) - lastspace = string and self._nonctext(string[-1]) - lastcs = nextcs - uchunks.append(string) - return EMPTYSTRING.join(uchunks) - - # Rich comparison operators for equality only. BAW: does it make sense to - # have or explicitly disable <, <=, >, >= operators? - def __eq__(self, other): - # other may be a Header or a string. Both are fine so coerce - # ourselves to a unicode (of the unencoded header value), swap the - # args and do another comparison. - return other == str(self) - - def __ne__(self, other): - return not self == other - - def append(self, s, charset=None, errors='strict'): - """Append a string to the MIME header. - - Optional charset, if given, should be a Charset instance or the name - of a character set (which will be converted to a Charset instance). A - value of None (the default) means that the charset given in the - constructor is used. - - s may be a byte string or a Unicode string. If it is a byte string - (i.e. isinstance(s, str) is false), then charset is the encoding of - that byte string, and a UnicodeError will be raised if the string - cannot be decoded with that charset. If s is a Unicode string, then - charset is a hint specifying the character set of the characters in - the string. In either case, when producing an RFC 2822 compliant - header using RFC 2047 rules, the string will be encoded using the - output codec of the charset. If the string cannot be encoded to the - output codec, a UnicodeError will be raised. - - Optional `errors' is passed as the errors argument to the decode - call if s is a byte string. - """ - if charset is None: - charset = self._charset - elif not isinstance(charset, Charset): - charset = Charset(charset) - if not isinstance(s, str): - input_charset = charset.input_codec or 'us-ascii' - if input_charset == _charset.UNKNOWN8BIT: - s = s.decode('us-ascii', 'surrogateescape') - else: - s = s.decode(input_charset, errors) - # Ensure that the bytes we're storing can be decoded to the output - # character set, otherwise an early error is raised. - output_charset = charset.output_codec or 'us-ascii' - if output_charset != _charset.UNKNOWN8BIT: - try: - s.encode(output_charset, errors) - except UnicodeEncodeError: - if output_charset!='us-ascii': - raise - charset = UTF8 - self._chunks.append((s, charset)) - - def _nonctext(self, s): - """True if string s is not a ctext character of RFC822. - """ - return s.isspace() or s in ('(', ')', '\\') - - def encode(self, splitchars=';, \t', maxlinelen=None, linesep='\n'): - r"""Encode a message header into an RFC-compliant format. - - There are many issues involved in converting a given string for use in - an email header. Only certain character sets are readable in most - email clients, and as header strings can only contain a subset of - 7-bit ASCII, care must be taken to properly convert and encode (with - Base64 or quoted-printable) header strings. In addition, there is a - 75-character length limit on any given encoded header field, so - line-wrapping must be performed, even with double-byte character sets. - - Optional maxlinelen specifies the maximum length of each generated - line, exclusive of the linesep string. Individual lines may be longer - than maxlinelen if a folding point cannot be found. The first line - will be shorter by the length of the header name plus ": " if a header - name was specified at Header construction time. The default value for - maxlinelen is determined at header construction time. - - Optional splitchars is a string containing characters which should be - given extra weight by the splitting algorithm during normal header - wrapping. This is in very rough support of RFC 2822's `higher level - syntactic breaks': split points preceded by a splitchar are preferred - during line splitting, with the characters preferred in the order in - which they appear in the string. Space and tab may be included in the - string to indicate whether preference should be given to one over the - other as a split point when other split chars do not appear in the line - being split. Splitchars does not affect RFC 2047 encoded lines. - - Optional linesep is a string to be used to separate the lines of - the value. The default value is the most useful for typical - Python applications, but it can be set to \r\n to produce RFC-compliant - line separators when needed. - """ - self._normalize() - if maxlinelen is None: - maxlinelen = self._maxlinelen - # A maxlinelen of 0 means don't wrap. For all practical purposes, - # choosing a huge number here accomplishes that and makes the - # _ValueFormatter algorithm much simpler. - if maxlinelen == 0: - maxlinelen = 1000000 - formatter = _ValueFormatter(self._headerlen, maxlinelen, - self._continuation_ws, splitchars) - lastcs = None - hasspace = lastspace = None - for string, charset in self._chunks: - if hasspace is not None: - hasspace = string and self._nonctext(string[0]) - import sys - if lastcs not in (None, 'us-ascii'): - if not hasspace or charset not in (None, 'us-ascii'): - formatter.add_transition() - elif charset not in (None, 'us-ascii') and not lastspace: - formatter.add_transition() - lastspace = string and self._nonctext(string[-1]) - lastcs = charset - hasspace = False - lines = string.splitlines() - if lines: - formatter.feed('', lines[0], charset) - else: - formatter.feed('', '', charset) - for line in lines[1:]: - formatter.newline() - if charset.header_encoding is not None: - formatter.feed(self._continuation_ws, ' ' + line.lstrip(), - charset) - else: - sline = line.lstrip() - fws = line[:len(line)-len(sline)] - formatter.feed(fws, sline, charset) - if len(lines) > 1: - formatter.newline() - if self._chunks: - formatter.add_transition() - value = formatter._str(linesep) - if _embeded_header.search(value): - raise HeaderParseError("header value appears to contain " - "an embedded header: {!r}".format(value)) - return value - - def _normalize(self): - # Step 1: Normalize the chunks so that all runs of identical charsets - # get collapsed into a single unicode string. - chunks = [] - last_charset = None - last_chunk = [] - for string, charset in self._chunks: - if charset == last_charset: - last_chunk.append(string) - else: - if last_charset is not None: - chunks.append((SPACE.join(last_chunk), last_charset)) - last_chunk = [string] - last_charset = charset - if last_chunk: - chunks.append((SPACE.join(last_chunk), last_charset)) - self._chunks = chunks - - -class _ValueFormatter(object): - def __init__(self, headerlen, maxlen, continuation_ws, splitchars): - self._maxlen = maxlen - self._continuation_ws = continuation_ws - self._continuation_ws_len = len(continuation_ws) - self._splitchars = splitchars - self._lines = [] - self._current_line = _Accumulator(headerlen) - - def _str(self, linesep): - self.newline() - return linesep.join(self._lines) - - def __str__(self): - return self._str(NL) - - def newline(self): - end_of_line = self._current_line.pop() - if end_of_line != (' ', ''): - self._current_line.push(*end_of_line) - if len(self._current_line) > 0: - if self._current_line.is_onlyws(): - self._lines[-1] += str(self._current_line) - else: - self._lines.append(str(self._current_line)) - self._current_line.reset() - - def add_transition(self): - self._current_line.push(' ', '') - - def feed(self, fws, string, charset): - # If the charset has no header encoding (i.e. it is an ASCII encoding) - # then we must split the header at the "highest level syntactic break" - # possible. Note that we don't have a lot of smarts about field - # syntax; we just try to break on semi-colons, then commas, then - # whitespace. Eventually, this should be pluggable. - if charset.header_encoding is None: - self._ascii_split(fws, string, self._splitchars) - return - # Otherwise, we're doing either a Base64 or a quoted-printable - # encoding which means we don't need to split the line on syntactic - # breaks. We can basically just find enough characters to fit on the - # current line, minus the RFC 2047 chrome. What makes this trickier - # though is that we have to split at octet boundaries, not character - # boundaries but it's only safe to split at character boundaries so at - # best we can only get close. - encoded_lines = charset.header_encode_lines(string, self._maxlengths()) - # The first element extends the current line, but if it's None then - # nothing more fit on the current line so start a new line. - try: - first_line = encoded_lines.pop(0) - except IndexError: - # There are no encoded lines, so we're done. - return - if first_line is not None: - self._append_chunk(fws, first_line) - try: - last_line = encoded_lines.pop() - except IndexError: - # There was only one line. - return - self.newline() - self._current_line.push(self._continuation_ws, last_line) - # Everything else are full lines in themselves. - for line in encoded_lines: - self._lines.append(self._continuation_ws + line) - - def _maxlengths(self): - # The first line's length. - yield self._maxlen - len(self._current_line) - while True: - yield self._maxlen - self._continuation_ws_len - - def _ascii_split(self, fws, string, splitchars): - # The RFC 2822 header folding algorithm is simple in principle but - # complex in practice. Lines may be folded any place where "folding - # white space" appears by inserting a linesep character in front of the - # FWS. The complication is that not all spaces or tabs qualify as FWS, - # and we are also supposed to prefer to break at "higher level - # syntactic breaks". We can't do either of these without intimate - # knowledge of the structure of structured headers, which we don't have - # here. So the best we can do here is prefer to break at the specified - # splitchars, and hope that we don't choose any spaces or tabs that - # aren't legal FWS. (This is at least better than the old algorithm, - # where we would sometimes *introduce* FWS after a splitchar, or the - # algorithm before that, where we would turn all white space runs into - # single spaces or tabs.) - parts = re.split("(["+FWS+"]+)", fws+string) - if parts[0]: - parts[:0] = [''] - else: - parts.pop(0) - for fws, part in zip(*[iter(parts)]*2): - self._append_chunk(fws, part) - - def _append_chunk(self, fws, string): - self._current_line.push(fws, string) - if len(self._current_line) > self._maxlen: - # Find the best split point, working backward from the end. - # There might be none, on a long first line. - for ch in self._splitchars: - for i in range(self._current_line.part_count()-1, 0, -1): - if ch.isspace(): - fws = self._current_line[i][0] - if fws and fws[0]==ch: - break - prevpart = self._current_line[i-1][1] - if prevpart and prevpart[-1]==ch: - break - else: - continue - break - else: - fws, part = self._current_line.pop() - if self._current_line._initial_size > 0: - # There will be a header, so leave it on a line by itself. - self.newline() - if not fws: - # We don't use continuation_ws here because the whitespace - # after a header should always be a space. - fws = ' ' - self._current_line.push(fws, part) - return - remainder = self._current_line.pop_from(i) - self._lines.append(str(self._current_line)) - self._current_line.reset(remainder) - - -class _Accumulator(list): - - def __init__(self, initial_size=0): - self._initial_size = initial_size - super().__init__() - - def push(self, fws, string): - self.append((fws, string)) - - def pop_from(self, i=0): - popped = self[i:] - self[i:] = [] - return popped - - def pop(self): - if self.part_count()==0: - return ('', '') - return super().pop() - - def __len__(self): - return sum((len(fws)+len(part) for fws, part in self), - self._initial_size) - - def __str__(self): - return EMPTYSTRING.join((EMPTYSTRING.join((fws, part)) - for fws, part in self)) - - def reset(self, startval=None): - if startval is None: - startval = [] - self[:] = startval - self._initial_size = 0 - - def is_onlyws(self): - return self._initial_size==0 and (not self or str(self).isspace()) - - def part_count(self): - return super().__len__() diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/headerregistry.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/headerregistry.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/headerregistry.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/headerregistry.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,592 +0,0 @@ -"""Representing and manipulating email headers via custom objects. - -This module provides an implementation of the HeaderRegistry API. -The implementation is designed to flexibly follow RFC5322 rules. - -Eventually HeaderRegistry will be a public API, but it isn't yet, -and will probably change some before that happens. - -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -from future.builtins import super -from future.builtins import str -from future.utils import text_to_native_str -from future.backports.email import utils -from future.backports.email import errors -from future.backports.email import _header_value_parser as parser - -class Address(object): - - def __init__(self, display_name='', username='', domain='', addr_spec=None): - """Create an object represeting a full email address. - - An address can have a 'display_name', a 'username', and a 'domain'. In - addition to specifying the username and domain separately, they may be - specified together by using the addr_spec keyword *instead of* the - username and domain keywords. If an addr_spec string is specified it - must be properly quoted according to RFC 5322 rules; an error will be - raised if it is not. - - An Address object has display_name, username, domain, and addr_spec - attributes, all of which are read-only. The addr_spec and the string - value of the object are both quoted according to RFC5322 rules, but - without any Content Transfer Encoding. - - """ - # This clause with its potential 'raise' may only happen when an - # application program creates an Address object using an addr_spec - # keyword. The email library code itself must always supply username - # and domain. - if addr_spec is not None: - if username or domain: - raise TypeError("addrspec specified when username and/or " - "domain also specified") - a_s, rest = parser.get_addr_spec(addr_spec) - if rest: - raise ValueError("Invalid addr_spec; only '{}' " - "could be parsed from '{}'".format( - a_s, addr_spec)) - if a_s.all_defects: - raise a_s.all_defects[0] - username = a_s.local_part - domain = a_s.domain - self._display_name = display_name - self._username = username - self._domain = domain - - @property - def display_name(self): - return self._display_name - - @property - def username(self): - return self._username - - @property - def domain(self): - return self._domain - - @property - def addr_spec(self): - """The addr_spec (username@domain) portion of the address, quoted - according to RFC 5322 rules, but with no Content Transfer Encoding. - """ - nameset = set(self.username) - if len(nameset) > len(nameset-parser.DOT_ATOM_ENDS): - lp = parser.quote_string(self.username) - else: - lp = self.username - if self.domain: - return lp + '@' + self.domain - if not lp: - return '<>' - return lp - - def __repr__(self): - return "Address(display_name={!r}, username={!r}, domain={!r})".format( - self.display_name, self.username, self.domain) - - def __str__(self): - nameset = set(self.display_name) - if len(nameset) > len(nameset-parser.SPECIALS): - disp = parser.quote_string(self.display_name) - else: - disp = self.display_name - if disp: - addr_spec = '' if self.addr_spec=='<>' else self.addr_spec - return "{} <{}>".format(disp, addr_spec) - return self.addr_spec - - def __eq__(self, other): - if type(other) != type(self): - return False - return (self.display_name == other.display_name and - self.username == other.username and - self.domain == other.domain) - - -class Group(object): - - def __init__(self, display_name=None, addresses=None): - """Create an object representing an address group. - - An address group consists of a display_name followed by colon and an - list of addresses (see Address) terminated by a semi-colon. The Group - is created by specifying a display_name and a possibly empty list of - Address objects. A Group can also be used to represent a single - address that is not in a group, which is convenient when manipulating - lists that are a combination of Groups and individual Addresses. In - this case the display_name should be set to None. In particular, the - string representation of a Group whose display_name is None is the same - as the Address object, if there is one and only one Address object in - the addresses list. - - """ - self._display_name = display_name - self._addresses = tuple(addresses) if addresses else tuple() - - @property - def display_name(self): - return self._display_name - - @property - def addresses(self): - return self._addresses - - def __repr__(self): - return "Group(display_name={!r}, addresses={!r}".format( - self.display_name, self.addresses) - - def __str__(self): - if self.display_name is None and len(self.addresses)==1: - return str(self.addresses[0]) - disp = self.display_name - if disp is not None: - nameset = set(disp) - if len(nameset) > len(nameset-parser.SPECIALS): - disp = parser.quote_string(disp) - adrstr = ", ".join(str(x) for x in self.addresses) - adrstr = ' ' + adrstr if adrstr else adrstr - return "{}:{};".format(disp, adrstr) - - def __eq__(self, other): - if type(other) != type(self): - return False - return (self.display_name == other.display_name and - self.addresses == other.addresses) - - -# Header Classes # - -class BaseHeader(str): - - """Base class for message headers. - - Implements generic behavior and provides tools for subclasses. - - A subclass must define a classmethod named 'parse' that takes an unfolded - value string and a dictionary as its arguments. The dictionary will - contain one key, 'defects', initialized to an empty list. After the call - the dictionary must contain two additional keys: parse_tree, set to the - parse tree obtained from parsing the header, and 'decoded', set to the - string value of the idealized representation of the data from the value. - (That is, encoded words are decoded, and values that have canonical - representations are so represented.) - - The defects key is intended to collect parsing defects, which the message - parser will subsequently dispose of as appropriate. The parser should not, - insofar as practical, raise any errors. Defects should be added to the - list instead. The standard header parsers register defects for RFC - compliance issues, for obsolete RFC syntax, and for unrecoverable parsing - errors. - - The parse method may add additional keys to the dictionary. In this case - the subclass must define an 'init' method, which will be passed the - dictionary as its keyword arguments. The method should use (usually by - setting them as the value of similarly named attributes) and remove all the - extra keys added by its parse method, and then use super to call its parent - class with the remaining arguments and keywords. - - The subclass should also make sure that a 'max_count' attribute is defined - that is either None or 1. XXX: need to better define this API. - - """ - - def __new__(cls, name, value): - kwds = {'defects': []} - cls.parse(value, kwds) - if utils._has_surrogates(kwds['decoded']): - kwds['decoded'] = utils._sanitize(kwds['decoded']) - self = str.__new__(cls, kwds['decoded']) - # del kwds['decoded'] - self.init(name, **kwds) - return self - - def init(self, name, **_3to2kwargs): - defects = _3to2kwargs['defects']; del _3to2kwargs['defects'] - parse_tree = _3to2kwargs['parse_tree']; del _3to2kwargs['parse_tree'] - self._name = name - self._parse_tree = parse_tree - self._defects = defects - - @property - def name(self): - return self._name - - @property - def defects(self): - return tuple(self._defects) - - def __reduce__(self): - return ( - _reconstruct_header, - ( - self.__class__.__name__, - self.__class__.__bases__, - str(self), - ), - self.__dict__) - - @classmethod - def _reconstruct(cls, value): - return str.__new__(cls, value) - - def fold(self, **_3to2kwargs): - policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - """Fold header according to policy. - - The parsed representation of the header is folded according to - RFC5322 rules, as modified by the policy. If the parse tree - contains surrogateescaped bytes, the bytes are CTE encoded using - the charset 'unknown-8bit". - - Any non-ASCII characters in the parse tree are CTE encoded using - charset utf-8. XXX: make this a policy setting. - - The returned value is an ASCII-only string possibly containing linesep - characters, and ending with a linesep character. The string includes - the header name and the ': ' separator. - - """ - # At some point we need to only put fws here if it was in the source. - header = parser.Header([ - parser.HeaderLabel([ - parser.ValueTerminal(self.name, 'header-name'), - parser.ValueTerminal(':', 'header-sep')]), - parser.CFWSList([parser.WhiteSpaceTerminal(' ', 'fws')]), - self._parse_tree]) - return header.fold(policy=policy) - - -def _reconstruct_header(cls_name, bases, value): - return type(text_to_native_str(cls_name), bases, {})._reconstruct(value) - - -class UnstructuredHeader(object): - - max_count = None - value_parser = staticmethod(parser.get_unstructured) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = cls.value_parser(value) - kwds['decoded'] = str(kwds['parse_tree']) - - -class UniqueUnstructuredHeader(UnstructuredHeader): - - max_count = 1 - - -class DateHeader(object): - - """Header whose value consists of a single timestamp. - - Provides an additional attribute, datetime, which is either an aware - datetime using a timezone, or a naive datetime if the timezone - in the input string is -0000. Also accepts a datetime as input. - The 'value' attribute is the normalized form of the timestamp, - which means it is the output of format_datetime on the datetime. - """ - - max_count = None - - # This is used only for folding, not for creating 'decoded'. - value_parser = staticmethod(parser.get_unstructured) - - @classmethod - def parse(cls, value, kwds): - if not value: - kwds['defects'].append(errors.HeaderMissingRequiredValue()) - kwds['datetime'] = None - kwds['decoded'] = '' - kwds['parse_tree'] = parser.TokenList() - return - if isinstance(value, str): - value = utils.parsedate_to_datetime(value) - kwds['datetime'] = value - kwds['decoded'] = utils.format_datetime(kwds['datetime']) - kwds['parse_tree'] = cls.value_parser(kwds['decoded']) - - def init(self, *args, **kw): - self._datetime = kw.pop('datetime') - super().init(*args, **kw) - - @property - def datetime(self): - return self._datetime - - -class UniqueDateHeader(DateHeader): - - max_count = 1 - - -class AddressHeader(object): - - max_count = None - - @staticmethod - def value_parser(value): - address_list, value = parser.get_address_list(value) - assert not value, 'this should not happen' - return address_list - - @classmethod - def parse(cls, value, kwds): - if isinstance(value, str): - # We are translating here from the RFC language (address/mailbox) - # to our API language (group/address). - kwds['parse_tree'] = address_list = cls.value_parser(value) - groups = [] - for addr in address_list.addresses: - groups.append(Group(addr.display_name, - [Address(mb.display_name or '', - mb.local_part or '', - mb.domain or '') - for mb in addr.all_mailboxes])) - defects = list(address_list.all_defects) - else: - # Assume it is Address/Group stuff - if not hasattr(value, '__iter__'): - value = [value] - groups = [Group(None, [item]) if not hasattr(item, 'addresses') - else item - for item in value] - defects = [] - kwds['groups'] = groups - kwds['defects'] = defects - kwds['decoded'] = ', '.join([str(item) for item in groups]) - if 'parse_tree' not in kwds: - kwds['parse_tree'] = cls.value_parser(kwds['decoded']) - - def init(self, *args, **kw): - self._groups = tuple(kw.pop('groups')) - self._addresses = None - super().init(*args, **kw) - - @property - def groups(self): - return self._groups - - @property - def addresses(self): - if self._addresses is None: - self._addresses = tuple([address for group in self._groups - for address in group.addresses]) - return self._addresses - - -class UniqueAddressHeader(AddressHeader): - - max_count = 1 - - -class SingleAddressHeader(AddressHeader): - - @property - def address(self): - if len(self.addresses)!=1: - raise ValueError(("value of single address header {} is not " - "a single address").format(self.name)) - return self.addresses[0] - - -class UniqueSingleAddressHeader(SingleAddressHeader): - - max_count = 1 - - -class MIMEVersionHeader(object): - - max_count = 1 - - value_parser = staticmethod(parser.parse_mime_version) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - kwds['major'] = None if parse_tree.minor is None else parse_tree.major - kwds['minor'] = parse_tree.minor - if parse_tree.minor is not None: - kwds['version'] = '{}.{}'.format(kwds['major'], kwds['minor']) - else: - kwds['version'] = None - - def init(self, *args, **kw): - self._version = kw.pop('version') - self._major = kw.pop('major') - self._minor = kw.pop('minor') - super().init(*args, **kw) - - @property - def major(self): - return self._major - - @property - def minor(self): - return self._minor - - @property - def version(self): - return self._version - - -class ParameterizedMIMEHeader(object): - - # Mixin that handles the params dict. Must be subclassed and - # a property value_parser for the specific header provided. - - max_count = 1 - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - if parse_tree.params is None: - kwds['params'] = {} - else: - # The MIME RFCs specify that parameter ordering is arbitrary. - kwds['params'] = dict((utils._sanitize(name).lower(), - utils._sanitize(value)) - for name, value in parse_tree.params) - - def init(self, *args, **kw): - self._params = kw.pop('params') - super().init(*args, **kw) - - @property - def params(self): - return self._params.copy() - - -class ContentTypeHeader(ParameterizedMIMEHeader): - - value_parser = staticmethod(parser.parse_content_type_header) - - def init(self, *args, **kw): - super().init(*args, **kw) - self._maintype = utils._sanitize(self._parse_tree.maintype) - self._subtype = utils._sanitize(self._parse_tree.subtype) - - @property - def maintype(self): - return self._maintype - - @property - def subtype(self): - return self._subtype - - @property - def content_type(self): - return self.maintype + '/' + self.subtype - - -class ContentDispositionHeader(ParameterizedMIMEHeader): - - value_parser = staticmethod(parser.parse_content_disposition_header) - - def init(self, *args, **kw): - super().init(*args, **kw) - cd = self._parse_tree.content_disposition - self._content_disposition = cd if cd is None else utils._sanitize(cd) - - @property - def content_disposition(self): - return self._content_disposition - - -class ContentTransferEncodingHeader(object): - - max_count = 1 - - value_parser = staticmethod(parser.parse_content_transfer_encoding_header) - - @classmethod - def parse(cls, value, kwds): - kwds['parse_tree'] = parse_tree = cls.value_parser(value) - kwds['decoded'] = str(parse_tree) - kwds['defects'].extend(parse_tree.all_defects) - - def init(self, *args, **kw): - super().init(*args, **kw) - self._cte = utils._sanitize(self._parse_tree.cte) - - @property - def cte(self): - return self._cte - - -# The header factory # - -_default_header_map = { - 'subject': UniqueUnstructuredHeader, - 'date': UniqueDateHeader, - 'resent-date': DateHeader, - 'orig-date': UniqueDateHeader, - 'sender': UniqueSingleAddressHeader, - 'resent-sender': SingleAddressHeader, - 'to': UniqueAddressHeader, - 'resent-to': AddressHeader, - 'cc': UniqueAddressHeader, - 'resent-cc': AddressHeader, - 'bcc': UniqueAddressHeader, - 'resent-bcc': AddressHeader, - 'from': UniqueAddressHeader, - 'resent-from': AddressHeader, - 'reply-to': UniqueAddressHeader, - 'mime-version': MIMEVersionHeader, - 'content-type': ContentTypeHeader, - 'content-disposition': ContentDispositionHeader, - 'content-transfer-encoding': ContentTransferEncodingHeader, - } - -class HeaderRegistry(object): - - """A header_factory and header registry.""" - - def __init__(self, base_class=BaseHeader, default_class=UnstructuredHeader, - use_default_map=True): - """Create a header_factory that works with the Policy API. - - base_class is the class that will be the last class in the created - header class's __bases__ list. default_class is the class that will be - used if "name" (see __call__) does not appear in the registry. - use_default_map controls whether or not the default mapping of names to - specialized classes is copied in to the registry when the factory is - created. The default is True. - - """ - self.registry = {} - self.base_class = base_class - self.default_class = default_class - if use_default_map: - self.registry.update(_default_header_map) - - def map_to_type(self, name, cls): - """Register cls as the specialized class for handling "name" headers. - - """ - self.registry[name.lower()] = cls - - def __getitem__(self, name): - cls = self.registry.get(name.lower(), self.default_class) - return type(text_to_native_str('_'+cls.__name__), (cls, self.base_class), {}) - - def __call__(self, name, value): - """Create a header instance for header 'name' from 'value'. - - Creates a header instance by creating a specialized class for parsing - and representing the specified header by combining the factory - base_class with a specialized class from the registry or the - default_class, and passing the name and value to the constructed - class's constructor. - - """ - return self[name](name, value) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_header_value_parser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_header_value_parser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_header_value_parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_header_value_parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2965 +0,0 @@ -"""Header value parser implementing various email-related RFC parsing rules. - -The parsing methods defined in this module implement various email related -parsing rules. Principal among them is RFC 5322, which is the followon -to RFC 2822 and primarily a clarification of the former. It also implements -RFC 2047 encoded word decoding. - -RFC 5322 goes to considerable trouble to maintain backward compatibility with -RFC 822 in the parse phase, while cleaning up the structure on the generation -phase. This parser supports correct RFC 5322 generation by tagging white space -as folding white space only when folding is allowed in the non-obsolete rule -sets. Actually, the parser is even more generous when accepting input than RFC -5322 mandates, following the spirit of Postel's Law, which RFC 5322 encourages. -Where possible deviations from the standard are annotated on the 'defects' -attribute of tokens that deviate. - -The general structure of the parser follows RFC 5322, and uses its terminology -where there is a direct correspondence. Where the implementation requires a -somewhat different structure than that used by the formal grammar, new terms -that mimic the closest existing terms are used. Thus, it really helps to have -a copy of RFC 5322 handy when studying this code. - -Input to the parser is a string that has already been unfolded according to -RFC 5322 rules. According to the RFC this unfolding is the very first step, and -this parser leaves the unfolding step to a higher level message parser, which -will have already detected the line breaks that need unfolding while -determining the beginning and end of each header. - -The output of the parser is a TokenList object, which is a list subclass. A -TokenList is a recursive data structure. The terminal nodes of the structure -are Terminal objects, which are subclasses of str. These do not correspond -directly to terminal objects in the formal grammar, but are instead more -practical higher level combinations of true terminals. - -All TokenList and Terminal objects have a 'value' attribute, which produces the -semantically meaningful value of that part of the parse subtree. The value of -all whitespace tokens (no matter how many sub-tokens they may contain) is a -single space, as per the RFC rules. This includes 'CFWS', which is herein -included in the general class of whitespace tokens. There is one exception to -the rule that whitespace tokens are collapsed into single spaces in values: in -the value of a 'bare-quoted-string' (a quoted-string with no leading or -trailing whitespace), any whitespace that appeared between the quotation marks -is preserved in the returned value. Note that in all Terminal strings quoted -pairs are turned into their unquoted values. - -All TokenList and Terminal objects also have a string value, which attempts to -be a "canonical" representation of the RFC-compliant form of the substring that -produced the parsed subtree, including minimal use of quoted pair quoting. -Whitespace runs are not collapsed. - -Comment tokens also have a 'content' attribute providing the string found -between the parens (including any nested comments) with whitespace preserved. - -All TokenList and Terminal objects have a 'defects' attribute which is a -possibly empty list all of the defects found while creating the token. Defects -may appear on any token in the tree, and a composite list of all defects in the -subtree is available through the 'all_defects' attribute of any node. (For -Terminal notes x.defects == x.all_defects.) - -Each object in a parse tree is called a 'token', and each has a 'token_type' -attribute that gives the name from the RFC 5322 grammar that it represents. -Not all RFC 5322 nodes are produced, and there is one non-RFC 5322 node that -may be produced: 'ptext'. A 'ptext' is a string of printable ascii characters. -It is returned in place of lists of (ctext/quoted-pair) and -(qtext/quoted-pair). - -XXX: provide complete list of token types. -""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import int, range, str, super, list - -import re -from collections import namedtuple, OrderedDict - -from future.backports.urllib.parse import (unquote, unquote_to_bytes) -from future.backports.email import _encoded_words as _ew -from future.backports.email import errors -from future.backports.email import utils - -# -# Useful constants and functions -# - -WSP = set(' \t') -CFWS_LEADER = WSP | set('(') -SPECIALS = set(r'()<>@,:;.\"[]') -ATOM_ENDS = SPECIALS | WSP -DOT_ATOM_ENDS = ATOM_ENDS - set('.') -# '.', '"', and '(' do not end phrases in order to support obs-phrase -PHRASE_ENDS = SPECIALS - set('."(') -TSPECIALS = (SPECIALS | set('/?=')) - set('.') -TOKEN_ENDS = TSPECIALS | WSP -ASPECIALS = TSPECIALS | set("*'%") -ATTRIBUTE_ENDS = ASPECIALS | WSP -EXTENDED_ATTRIBUTE_ENDS = ATTRIBUTE_ENDS - set('%') - -def quote_string(value): - return '"'+str(value).replace('\\', '\\\\').replace('"', r'\"')+'"' - -# -# Accumulator for header folding -# - -class _Folded(object): - - def __init__(self, maxlen, policy): - self.maxlen = maxlen - self.policy = policy - self.lastlen = 0 - self.stickyspace = None - self.firstline = True - self.done = [] - self.current = list() # uses l.clear() - - def newline(self): - self.done.extend(self.current) - self.done.append(self.policy.linesep) - self.current.clear() - self.lastlen = 0 - - def finalize(self): - if self.current: - self.newline() - - def __str__(self): - return ''.join(self.done) - - def append(self, stoken): - self.current.append(stoken) - - def append_if_fits(self, token, stoken=None): - if stoken is None: - stoken = str(token) - l = len(stoken) - if self.stickyspace is not None: - stickyspace_len = len(self.stickyspace) - if self.lastlen + stickyspace_len + l <= self.maxlen: - self.current.append(self.stickyspace) - self.lastlen += stickyspace_len - self.current.append(stoken) - self.lastlen += l - self.stickyspace = None - self.firstline = False - return True - if token.has_fws: - ws = token.pop_leading_fws() - if ws is not None: - self.stickyspace += str(ws) - stickyspace_len += len(ws) - token._fold(self) - return True - if stickyspace_len and l + 1 <= self.maxlen: - margin = self.maxlen - l - if 0 < margin < stickyspace_len: - trim = stickyspace_len - margin - self.current.append(self.stickyspace[:trim]) - self.stickyspace = self.stickyspace[trim:] - stickyspace_len = trim - self.newline() - self.current.append(self.stickyspace) - self.current.append(stoken) - self.lastlen = l + stickyspace_len - self.stickyspace = None - self.firstline = False - return True - if not self.firstline: - self.newline() - self.current.append(self.stickyspace) - self.current.append(stoken) - self.stickyspace = None - self.firstline = False - return True - if self.lastlen + l <= self.maxlen: - self.current.append(stoken) - self.lastlen += l - return True - if l < self.maxlen: - self.newline() - self.current.append(stoken) - self.lastlen = l - return True - return False - -# -# TokenList and its subclasses -# - -class TokenList(list): - - token_type = None - - def __init__(self, *args, **kw): - super(TokenList, self).__init__(*args, **kw) - self.defects = [] - - def __str__(self): - return ''.join(str(x) for x in self) - - def __repr__(self): - return '{}({})'.format(self.__class__.__name__, - super(TokenList, self).__repr__()) - - @property - def value(self): - return ''.join(x.value for x in self if x.value) - - @property - def all_defects(self): - return sum((x.all_defects for x in self), self.defects) - - # - # Folding API - # - # parts(): - # - # return a list of objects that constitute the "higher level syntactic - # objects" specified by the RFC as the best places to fold a header line. - # The returned objects must include leading folding white space, even if - # this means mutating the underlying parse tree of the object. Each object - # is only responsible for returning *its* parts, and should not drill down - # to any lower level except as required to meet the leading folding white - # space constraint. - # - # _fold(folded): - # - # folded: the result accumulator. This is an instance of _Folded. - # (XXX: I haven't finished factoring this out yet, the folding code - # pretty much uses this as a state object.) When the folded.current - # contains as much text as will fit, the _fold method should call - # folded.newline. - # folded.lastlen: the current length of the test stored in folded.current. - # folded.maxlen: The maximum number of characters that may appear on a - # folded line. Differs from the policy setting in that "no limit" is - # represented by +inf, which means it can be used in the trivially - # logical fashion in comparisons. - # - # Currently no subclasses implement parts, and I think this will remain - # true. A subclass only needs to implement _fold when the generic version - # isn't sufficient. _fold will need to be implemented primarily when it is - # possible for encoded words to appear in the specialized token-list, since - # there is no generic algorithm that can know where exactly the encoded - # words are allowed. A _fold implementation is responsible for filling - # lines in the same general way that the top level _fold does. It may, and - # should, call the _fold method of sub-objects in a similar fashion to that - # of the top level _fold. - # - # XXX: I'm hoping it will be possible to factor the existing code further - # to reduce redundancy and make the logic clearer. - - @property - def parts(self): - klass = self.__class__ - this = list() - for token in self: - if token.startswith_fws(): - if this: - yield this[0] if len(this)==1 else klass(this) - this.clear() - end_ws = token.pop_trailing_ws() - this.append(token) - if end_ws: - yield klass(this) - this = [end_ws] - if this: - yield this[0] if len(this)==1 else klass(this) - - def startswith_fws(self): - return self[0].startswith_fws() - - def pop_leading_fws(self): - if self[0].token_type == 'fws': - return self.pop(0) - return self[0].pop_leading_fws() - - def pop_trailing_ws(self): - if self[-1].token_type == 'cfws': - return self.pop(-1) - return self[-1].pop_trailing_ws() - - @property - def has_fws(self): - for part in self: - if part.has_fws: - return True - return False - - def has_leading_comment(self): - return self[0].has_leading_comment() - - @property - def comments(self): - comments = [] - for token in self: - comments.extend(token.comments) - return comments - - def fold(self, **_3to2kwargs): - # max_line_length 0/None means no limit, ie: infinitely long. - policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - maxlen = policy.max_line_length or float("+inf") - folded = _Folded(maxlen, policy) - self._fold(folded) - folded.finalize() - return str(folded) - - def as_encoded_word(self, charset): - # This works only for things returned by 'parts', which include - # the leading fws, if any, that should be used. - res = [] - ws = self.pop_leading_fws() - if ws: - res.append(ws) - trailer = self.pop(-1) if self[-1].token_type=='fws' else '' - res.append(_ew.encode(str(self), charset)) - res.append(trailer) - return ''.join(res) - - def cte_encode(self, charset, policy): - res = [] - for part in self: - res.append(part.cte_encode(charset, policy)) - return ''.join(res) - - def _fold(self, folded): - for part in self.parts: - tstr = str(part) - tlen = len(tstr) - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - # XXX: this should be a policy setting - charset = 'utf-8' - tstr = part.cte_encode(charset, folded.policy) - tlen = len(tstr) - if folded.append_if_fits(part, tstr): - continue - # Peel off the leading whitespace if any and make it sticky, to - # avoid infinite recursion. - ws = part.pop_leading_fws() - if ws is not None: - # Peel off the leading whitespace and make it sticky, to - # avoid infinite recursion. - folded.stickyspace = str(part.pop(0)) - if folded.append_if_fits(part): - continue - if part.has_fws: - part._fold(folded) - continue - # There are no fold points in this one; it is too long for a single - # line and can't be split...we just have to put it on its own line. - folded.append(tstr) - folded.newline() - - def pprint(self, indent=''): - print('\n'.join(self._pp(indent=''))) - - def ppstr(self, indent=''): - return '\n'.join(self._pp(indent='')) - - def _pp(self, indent=''): - yield '{}{}/{}('.format( - indent, - self.__class__.__name__, - self.token_type) - for token in self: - if not hasattr(token, '_pp'): - yield (indent + ' !! invalid element in token ' - 'list: {!r}'.format(token)) - else: - for line in token._pp(indent+' '): - yield line - if self.defects: - extra = ' Defects: {}'.format(self.defects) - else: - extra = '' - yield '{}){}'.format(indent, extra) - - -class WhiteSpaceTokenList(TokenList): - - @property - def value(self): - return ' ' - - @property - def comments(self): - return [x.content for x in self if x.token_type=='comment'] - - -class UnstructuredTokenList(TokenList): - - token_type = 'unstructured' - - def _fold(self, folded): - if any(x.token_type=='encoded-word' for x in self): - return self._fold_encoded(folded) - # Here we can have either a pure ASCII string that may or may not - # have surrogateescape encoded bytes, or a unicode string. - last_ew = None - for part in self.parts: - tstr = str(part) - is_ew = False - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - charset = 'utf-8' - if last_ew is not None: - # We've already done an EW, combine this one with it - # if there's room. - chunk = get_unstructured( - ''.join(folded.current[last_ew:]+[tstr])).as_encoded_word(charset) - oldlastlen = sum(len(x) for x in folded.current[:last_ew]) - schunk = str(chunk) - lchunk = len(schunk) - if oldlastlen + lchunk <= folded.maxlen: - del folded.current[last_ew:] - folded.append(schunk) - folded.lastlen = oldlastlen + lchunk - continue - tstr = part.as_encoded_word(charset) - is_ew = True - if folded.append_if_fits(part, tstr): - if is_ew: - last_ew = len(folded.current) - 1 - continue - if is_ew or last_ew: - # It's too big to fit on the line, but since we've - # got encoded words we can use encoded word folding. - part._fold_as_ew(folded) - continue - # Peel off the leading whitespace if any and make it sticky, to - # avoid infinite recursion. - ws = part.pop_leading_fws() - if ws is not None: - folded.stickyspace = str(ws) - if folded.append_if_fits(part): - continue - if part.has_fws: - part.fold(folded) - continue - # It can't be split...we just have to put it on its own line. - folded.append(tstr) - folded.newline() - last_ew = None - - def cte_encode(self, charset, policy): - res = [] - last_ew = None - for part in self: - spart = str(part) - try: - spart.encode('us-ascii') - res.append(spart) - except UnicodeEncodeError: - if last_ew is None: - res.append(part.cte_encode(charset, policy)) - last_ew = len(res) - else: - tl = get_unstructured(''.join(res[last_ew:] + [spart])) - res.append(tl.as_encoded_word()) - return ''.join(res) - - -class Phrase(TokenList): - - token_type = 'phrase' - - def _fold(self, folded): - # As with Unstructured, we can have pure ASCII with or without - # surrogateescape encoded bytes, or we could have unicode. But this - # case is more complicated, since we have to deal with the various - # sub-token types and how they can be composed in the face of - # unicode-that-needs-CTE-encoding, and the fact that if a token a - # comment that becomes a barrier across which we can't compose encoded - # words. - last_ew = None - for part in self.parts: - tstr = str(part) - tlen = len(tstr) - has_ew = False - try: - str(part).encode('us-ascii') - except UnicodeEncodeError: - if any(isinstance(x, errors.UndecodableBytesDefect) - for x in part.all_defects): - charset = 'unknown-8bit' - else: - charset = 'utf-8' - if last_ew is not None and not part.has_leading_comment(): - # We've already done an EW, let's see if we can combine - # this one with it. The last_ew logic ensures that all we - # have at this point is atoms, no comments or quoted - # strings. So we can treat the text between the last - # encoded word and the content of this token as - # unstructured text, and things will work correctly. But - # we have to strip off any trailing comment on this token - # first, and if it is a quoted string we have to pull out - # the content (we're encoding it, so it no longer needs to - # be quoted). - if part[-1].token_type == 'cfws' and part.comments: - remainder = part.pop(-1) - else: - remainder = '' - for i, token in enumerate(part): - if token.token_type == 'bare-quoted-string': - part[i] = UnstructuredTokenList(token[:]) - chunk = get_unstructured( - ''.join(folded.current[last_ew:]+[tstr])).as_encoded_word(charset) - schunk = str(chunk) - lchunk = len(schunk) - if last_ew + lchunk <= folded.maxlen: - del folded.current[last_ew:] - folded.append(schunk) - folded.lastlen = sum(len(x) for x in folded.current) - continue - tstr = part.as_encoded_word(charset) - tlen = len(tstr) - has_ew = True - if folded.append_if_fits(part, tstr): - if has_ew and not part.comments: - last_ew = len(folded.current) - 1 - elif part.comments or part.token_type == 'quoted-string': - # If a comment is involved we can't combine EWs. And if a - # quoted string is involved, it's not worth the effort to - # try to combine them. - last_ew = None - continue - part._fold(folded) - - def cte_encode(self, charset, policy): - res = [] - last_ew = None - is_ew = False - for part in self: - spart = str(part) - try: - spart.encode('us-ascii') - res.append(spart) - except UnicodeEncodeError: - is_ew = True - if last_ew is None: - if not part.comments: - last_ew = len(res) - res.append(part.cte_encode(charset, policy)) - elif not part.has_leading_comment(): - if part[-1].token_type == 'cfws' and part.comments: - remainder = part.pop(-1) - else: - remainder = '' - for i, token in enumerate(part): - if token.token_type == 'bare-quoted-string': - part[i] = UnstructuredTokenList(token[:]) - tl = get_unstructured(''.join(res[last_ew:] + [spart])) - res[last_ew:] = [tl.as_encoded_word(charset)] - if part.comments or (not is_ew and part.token_type == 'quoted-string'): - last_ew = None - return ''.join(res) - -class Word(TokenList): - - token_type = 'word' - - -class CFWSList(WhiteSpaceTokenList): - - token_type = 'cfws' - - def has_leading_comment(self): - return bool(self.comments) - - -class Atom(TokenList): - - token_type = 'atom' - - -class Token(TokenList): - - token_type = 'token' - - -class EncodedWord(TokenList): - - token_type = 'encoded-word' - cte = None - charset = None - lang = None - - @property - def encoded(self): - if self.cte is not None: - return self.cte - _ew.encode(str(self), self.charset) - - - -class QuotedString(TokenList): - - token_type = 'quoted-string' - - @property - def content(self): - for x in self: - if x.token_type == 'bare-quoted-string': - return x.value - - @property - def quoted_value(self): - res = [] - for x in self: - if x.token_type == 'bare-quoted-string': - res.append(str(x)) - else: - res.append(x.value) - return ''.join(res) - - @property - def stripped_value(self): - for token in self: - if token.token_type == 'bare-quoted-string': - return token.value - - -class BareQuotedString(QuotedString): - - token_type = 'bare-quoted-string' - - def __str__(self): - return quote_string(''.join(str(x) for x in self)) - - @property - def value(self): - return ''.join(str(x) for x in self) - - -class Comment(WhiteSpaceTokenList): - - token_type = 'comment' - - def __str__(self): - return ''.join(sum([ - ["("], - [self.quote(x) for x in self], - [")"], - ], [])) - - def quote(self, value): - if value.token_type == 'comment': - return str(value) - return str(value).replace('\\', '\\\\').replace( - '(', '\(').replace( - ')', '\)') - - @property - def content(self): - return ''.join(str(x) for x in self) - - @property - def comments(self): - return [self.content] - -class AddressList(TokenList): - - token_type = 'address-list' - - @property - def addresses(self): - return [x for x in self if x.token_type=='address'] - - @property - def mailboxes(self): - return sum((x.mailboxes - for x in self if x.token_type=='address'), []) - - @property - def all_mailboxes(self): - return sum((x.all_mailboxes - for x in self if x.token_type=='address'), []) - - -class Address(TokenList): - - token_type = 'address' - - @property - def display_name(self): - if self[0].token_type == 'group': - return self[0].display_name - - @property - def mailboxes(self): - if self[0].token_type == 'mailbox': - return [self[0]] - elif self[0].token_type == 'invalid-mailbox': - return [] - return self[0].mailboxes - - @property - def all_mailboxes(self): - if self[0].token_type == 'mailbox': - return [self[0]] - elif self[0].token_type == 'invalid-mailbox': - return [self[0]] - return self[0].all_mailboxes - -class MailboxList(TokenList): - - token_type = 'mailbox-list' - - @property - def mailboxes(self): - return [x for x in self if x.token_type=='mailbox'] - - @property - def all_mailboxes(self): - return [x for x in self - if x.token_type in ('mailbox', 'invalid-mailbox')] - - -class GroupList(TokenList): - - token_type = 'group-list' - - @property - def mailboxes(self): - if not self or self[0].token_type != 'mailbox-list': - return [] - return self[0].mailboxes - - @property - def all_mailboxes(self): - if not self or self[0].token_type != 'mailbox-list': - return [] - return self[0].all_mailboxes - - -class Group(TokenList): - - token_type = "group" - - @property - def mailboxes(self): - if self[2].token_type != 'group-list': - return [] - return self[2].mailboxes - - @property - def all_mailboxes(self): - if self[2].token_type != 'group-list': - return [] - return self[2].all_mailboxes - - @property - def display_name(self): - return self[0].display_name - - -class NameAddr(TokenList): - - token_type = 'name-addr' - - @property - def display_name(self): - if len(self) == 1: - return None - return self[0].display_name - - @property - def local_part(self): - return self[-1].local_part - - @property - def domain(self): - return self[-1].domain - - @property - def route(self): - return self[-1].route - - @property - def addr_spec(self): - return self[-1].addr_spec - - -class AngleAddr(TokenList): - - token_type = 'angle-addr' - - @property - def local_part(self): - for x in self: - if x.token_type == 'addr-spec': - return x.local_part - - @property - def domain(self): - for x in self: - if x.token_type == 'addr-spec': - return x.domain - - @property - def route(self): - for x in self: - if x.token_type == 'obs-route': - return x.domains - - @property - def addr_spec(self): - for x in self: - if x.token_type == 'addr-spec': - return x.addr_spec - else: - return '<>' - - -class ObsRoute(TokenList): - - token_type = 'obs-route' - - @property - def domains(self): - return [x.domain for x in self if x.token_type == 'domain'] - - -class Mailbox(TokenList): - - token_type = 'mailbox' - - @property - def display_name(self): - if self[0].token_type == 'name-addr': - return self[0].display_name - - @property - def local_part(self): - return self[0].local_part - - @property - def domain(self): - return self[0].domain - - @property - def route(self): - if self[0].token_type == 'name-addr': - return self[0].route - - @property - def addr_spec(self): - return self[0].addr_spec - - -class InvalidMailbox(TokenList): - - token_type = 'invalid-mailbox' - - @property - def display_name(self): - return None - - local_part = domain = route = addr_spec = display_name - - -class Domain(TokenList): - - token_type = 'domain' - - @property - def domain(self): - return ''.join(super(Domain, self).value.split()) - - -class DotAtom(TokenList): - - token_type = 'dot-atom' - - -class DotAtomText(TokenList): - - token_type = 'dot-atom-text' - - -class AddrSpec(TokenList): - - token_type = 'addr-spec' - - @property - def local_part(self): - return self[0].local_part - - @property - def domain(self): - if len(self) < 3: - return None - return self[-1].domain - - @property - def value(self): - if len(self) < 3: - return self[0].value - return self[0].value.rstrip()+self[1].value+self[2].value.lstrip() - - @property - def addr_spec(self): - nameset = set(self.local_part) - if len(nameset) > len(nameset-DOT_ATOM_ENDS): - lp = quote_string(self.local_part) - else: - lp = self.local_part - if self.domain is not None: - return lp + '@' + self.domain - return lp - - -class ObsLocalPart(TokenList): - - token_type = 'obs-local-part' - - -class DisplayName(Phrase): - - token_type = 'display-name' - - @property - def display_name(self): - res = TokenList(self) - if res[0].token_type == 'cfws': - res.pop(0) - else: - if res[0][0].token_type == 'cfws': - res[0] = TokenList(res[0][1:]) - if res[-1].token_type == 'cfws': - res.pop() - else: - if res[-1][-1].token_type == 'cfws': - res[-1] = TokenList(res[-1][:-1]) - return res.value - - @property - def value(self): - quote = False - if self.defects: - quote = True - else: - for x in self: - if x.token_type == 'quoted-string': - quote = True - if quote: - pre = post = '' - if self[0].token_type=='cfws' or self[0][0].token_type=='cfws': - pre = ' ' - if self[-1].token_type=='cfws' or self[-1][-1].token_type=='cfws': - post = ' ' - return pre+quote_string(self.display_name)+post - else: - return super(DisplayName, self).value - - -class LocalPart(TokenList): - - token_type = 'local-part' - - @property - def value(self): - if self[0].token_type == "quoted-string": - return self[0].quoted_value - else: - return self[0].value - - @property - def local_part(self): - # Strip whitespace from front, back, and around dots. - res = [DOT] - last = DOT - last_is_tl = False - for tok in self[0] + [DOT]: - if tok.token_type == 'cfws': - continue - if (last_is_tl and tok.token_type == 'dot' and - last[-1].token_type == 'cfws'): - res[-1] = TokenList(last[:-1]) - is_tl = isinstance(tok, TokenList) - if (is_tl and last.token_type == 'dot' and - tok[0].token_type == 'cfws'): - res.append(TokenList(tok[1:])) - else: - res.append(tok) - last = res[-1] - last_is_tl = is_tl - res = TokenList(res[1:-1]) - return res.value - - -class DomainLiteral(TokenList): - - token_type = 'domain-literal' - - @property - def domain(self): - return ''.join(super(DomainLiteral, self).value.split()) - - @property - def ip(self): - for x in self: - if x.token_type == 'ptext': - return x.value - - -class MIMEVersion(TokenList): - - token_type = 'mime-version' - major = None - minor = None - - -class Parameter(TokenList): - - token_type = 'parameter' - sectioned = False - extended = False - charset = 'us-ascii' - - @property - def section_number(self): - # Because the first token, the attribute (name) eats CFWS, the second - # token is always the section if there is one. - return self[1].number if self.sectioned else 0 - - @property - def param_value(self): - # This is part of the "handle quoted extended parameters" hack. - for token in self: - if token.token_type == 'value': - return token.stripped_value - if token.token_type == 'quoted-string': - for token in token: - if token.token_type == 'bare-quoted-string': - for token in token: - if token.token_type == 'value': - return token.stripped_value - return '' - - -class InvalidParameter(Parameter): - - token_type = 'invalid-parameter' - - -class Attribute(TokenList): - - token_type = 'attribute' - - @property - def stripped_value(self): - for token in self: - if token.token_type.endswith('attrtext'): - return token.value - -class Section(TokenList): - - token_type = 'section' - number = None - - -class Value(TokenList): - - token_type = 'value' - - @property - def stripped_value(self): - token = self[0] - if token.token_type == 'cfws': - token = self[1] - if token.token_type.endswith( - ('quoted-string', 'attribute', 'extended-attribute')): - return token.stripped_value - return self.value - - -class MimeParameters(TokenList): - - token_type = 'mime-parameters' - - @property - def params(self): - # The RFC specifically states that the ordering of parameters is not - # guaranteed and may be reordered by the transport layer. So we have - # to assume the RFC 2231 pieces can come in any order. However, we - # output them in the order that we first see a given name, which gives - # us a stable __str__. - params = OrderedDict() - for token in self: - if not token.token_type.endswith('parameter'): - continue - if token[0].token_type != 'attribute': - continue - name = token[0].value.strip() - if name not in params: - params[name] = [] - params[name].append((token.section_number, token)) - for name, parts in params.items(): - parts = sorted(parts) - # XXX: there might be more recovery we could do here if, for - # example, this is really a case of a duplicate attribute name. - value_parts = [] - charset = parts[0][1].charset - for i, (section_number, param) in enumerate(parts): - if section_number != i: - param.defects.append(errors.InvalidHeaderDefect( - "inconsistent multipart parameter numbering")) - value = param.param_value - if param.extended: - try: - value = unquote_to_bytes(value) - except UnicodeEncodeError: - # source had surrogate escaped bytes. What we do now - # is a bit of an open question. I'm not sure this is - # the best choice, but it is what the old algorithm did - value = unquote(value, encoding='latin-1') - else: - try: - value = value.decode(charset, 'surrogateescape') - except LookupError: - # XXX: there should really be a custom defect for - # unknown character set to make it easy to find, - # because otherwise unknown charset is a silent - # failure. - value = value.decode('us-ascii', 'surrogateescape') - if utils._has_surrogates(value): - param.defects.append(errors.UndecodableBytesDefect()) - value_parts.append(value) - value = ''.join(value_parts) - yield name, value - - def __str__(self): - params = [] - for name, value in self.params: - if value: - params.append('{}={}'.format(name, quote_string(value))) - else: - params.append(name) - params = '; '.join(params) - return ' ' + params if params else '' - - -class ParameterizedHeaderValue(TokenList): - - @property - def params(self): - for token in reversed(self): - if token.token_type == 'mime-parameters': - return token.params - return {} - - @property - def parts(self): - if self and self[-1].token_type == 'mime-parameters': - # We don't want to start a new line if all of the params don't fit - # after the value, so unwrap the parameter list. - return TokenList(self[:-1] + self[-1]) - return TokenList(self).parts - - -class ContentType(ParameterizedHeaderValue): - - token_type = 'content-type' - maintype = 'text' - subtype = 'plain' - - -class ContentDisposition(ParameterizedHeaderValue): - - token_type = 'content-disposition' - content_disposition = None - - -class ContentTransferEncoding(TokenList): - - token_type = 'content-transfer-encoding' - cte = '7bit' - - -class HeaderLabel(TokenList): - - token_type = 'header-label' - - -class Header(TokenList): - - token_type = 'header' - - def _fold(self, folded): - folded.append(str(self.pop(0))) - folded.lastlen = len(folded.current[0]) - # The first line of the header is different from all others: we don't - # want to start a new object on a new line if it has any fold points in - # it that would allow part of it to be on the first header line. - # Further, if the first fold point would fit on the new line, we want - # to do that, but if it doesn't we want to put it on the first line. - # Folded supports this via the stickyspace attribute. If this - # attribute is not None, it does the special handling. - folded.stickyspace = str(self.pop(0)) if self[0].token_type == 'cfws' else '' - rest = self.pop(0) - if self: - raise ValueError("Malformed Header token list") - rest._fold(folded) - - -# -# Terminal classes and instances -# - -class Terminal(str): - - def __new__(cls, value, token_type): - self = super(Terminal, cls).__new__(cls, value) - self.token_type = token_type - self.defects = [] - return self - - def __repr__(self): - return "{}({})".format(self.__class__.__name__, super(Terminal, self).__repr__()) - - @property - def all_defects(self): - return list(self.defects) - - def _pp(self, indent=''): - return ["{}{}/{}({}){}".format( - indent, - self.__class__.__name__, - self.token_type, - super(Terminal, self).__repr__(), - '' if not self.defects else ' {}'.format(self.defects), - )] - - def cte_encode(self, charset, policy): - value = str(self) - try: - value.encode('us-ascii') - return value - except UnicodeEncodeError: - return _ew.encode(value, charset) - - def pop_trailing_ws(self): - # This terminates the recursion. - return None - - def pop_leading_fws(self): - # This terminates the recursion. - return None - - @property - def comments(self): - return [] - - def has_leading_comment(self): - return False - - def __getnewargs__(self): - return(str(self), self.token_type) - - -class WhiteSpaceTerminal(Terminal): - - @property - def value(self): - return ' ' - - def startswith_fws(self): - return True - - has_fws = True - - -class ValueTerminal(Terminal): - - @property - def value(self): - return self - - def startswith_fws(self): - return False - - has_fws = False - - def as_encoded_word(self, charset): - return _ew.encode(str(self), charset) - - -class EWWhiteSpaceTerminal(WhiteSpaceTerminal): - - @property - def value(self): - return '' - - @property - def encoded(self): - return self[:] - - def __str__(self): - return '' - - has_fws = True - - -# XXX these need to become classes and used as instances so -# that a program can't change them in a parse tree and screw -# up other parse trees. Maybe should have tests for that, too. -DOT = ValueTerminal('.', 'dot') -ListSeparator = ValueTerminal(',', 'list-separator') -RouteComponentMarker = ValueTerminal('@', 'route-component-marker') - -# -# Parser -# - -"""Parse strings according to RFC822/2047/2822/5322 rules. - -This is a stateless parser. Each get_XXX function accepts a string and -returns either a Terminal or a TokenList representing the RFC object named -by the method and a string containing the remaining unparsed characters -from the input. Thus a parser method consumes the next syntactic construct -of a given type and returns a token representing the construct plus the -unparsed remainder of the input string. - -For example, if the first element of a structured header is a 'phrase', -then: - - phrase, value = get_phrase(value) - -returns the complete phrase from the start of the string value, plus any -characters left in the string after the phrase is removed. - -""" - -_wsp_splitter = re.compile(r'([{}]+)'.format(''.join(WSP))).split -_non_atom_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATOM_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_printable_finder = re.compile(r"[\x00-\x20\x7F]").findall -_non_token_end_matcher = re.compile(r"[^{}]+".format( - ''.join(TOKEN_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_attribute_end_matcher = re.compile(r"[^{}]+".format( - ''.join(ATTRIBUTE_ENDS).replace('\\','\\\\').replace(']','\]'))).match -_non_extended_attribute_end_matcher = re.compile(r"[^{}]+".format( - ''.join(EXTENDED_ATTRIBUTE_ENDS).replace( - '\\','\\\\').replace(']','\]'))).match - -def _validate_xtext(xtext): - """If input token contains ASCII non-printables, register a defect.""" - - non_printables = _non_printable_finder(xtext) - if non_printables: - xtext.defects.append(errors.NonPrintableDefect(non_printables)) - if utils._has_surrogates(xtext): - xtext.defects.append(errors.UndecodableBytesDefect( - "Non-ASCII characters found in header token")) - -def _get_ptext_to_endchars(value, endchars): - """Scan printables/quoted-pairs until endchars and return unquoted ptext. - - This function turns a run of qcontent, ccontent-without-comments, or - dtext-with-quoted-printables into a single string by unquoting any - quoted printables. It returns the string, the remaining value, and - a flag that is True iff there were any quoted printables decoded. - - """ - _3to2list = list(_wsp_splitter(value, 1)) - fragment, remainder, = _3to2list[:1] + [_3to2list[1:]] - vchars = [] - escape = False - had_qp = False - for pos in range(len(fragment)): - if fragment[pos] == '\\': - if escape: - escape = False - had_qp = True - else: - escape = True - continue - if escape: - escape = False - elif fragment[pos] in endchars: - break - vchars.append(fragment[pos]) - else: - pos = pos + 1 - return ''.join(vchars), ''.join([fragment[pos:]] + remainder), had_qp - -def _decode_ew_run(value): - """ Decode a run of RFC2047 encoded words. - - _decode_ew_run(value) -> (text, value, defects) - - Scans the supplied value for a run of tokens that look like they are RFC - 2047 encoded words, decodes those words into text according to RFC 2047 - rules (whitespace between encoded words is discarded), and returns the text - and the remaining value (including any leading whitespace on the remaining - value), as well as a list of any defects encountered while decoding. The - input value may not have any leading whitespace. - - """ - res = [] - defects = [] - last_ws = '' - while value: - try: - tok, ws, value = _wsp_splitter(value, 1) - except ValueError: - tok, ws, value = value, '', '' - if not (tok.startswith('=?') and tok.endswith('?=')): - return ''.join(res), last_ws + tok + ws + value, defects - text, charset, lang, new_defects = _ew.decode(tok) - res.append(text) - defects.extend(new_defects) - last_ws = ws - return ''.join(res), last_ws, defects - -def get_fws(value): - """FWS = 1*WSP - - This isn't the RFC definition. We're using fws to represent tokens where - folding can be done, but when we are parsing the *un*folding has already - been done so we don't need to watch out for CRLF. - - """ - newvalue = value.lstrip() - fws = WhiteSpaceTerminal(value[:len(value)-len(newvalue)], 'fws') - return fws, newvalue - -def get_encoded_word(value): - """ encoded-word = "=?" charset "?" encoding "?" encoded-text "?=" - - """ - ew = EncodedWord() - if not value.startswith('=?'): - raise errors.HeaderParseError( - "expected encoded word but found {}".format(value)) - _3to2list1 = list(value[2:].split('?=', 1)) - tok, remainder, = _3to2list1[:1] + [_3to2list1[1:]] - if tok == value[2:]: - raise errors.HeaderParseError( - "expected encoded word but found {}".format(value)) - remstr = ''.join(remainder) - if remstr[:2].isdigit(): - _3to2list3 = list(remstr.split('?=', 1)) - rest, remainder, = _3to2list3[:1] + [_3to2list3[1:]] - tok = tok + '?=' + rest - if len(tok.split()) > 1: - ew.defects.append(errors.InvalidHeaderDefect( - "whitespace inside encoded word")) - ew.cte = value - value = ''.join(remainder) - try: - text, charset, lang, defects = _ew.decode('=?' + tok + '?=') - except ValueError: - raise errors.HeaderParseError( - "encoded word format invalid: '{}'".format(ew.cte)) - ew.charset = charset - ew.lang = lang - ew.defects.extend(defects) - while text: - if text[0] in WSP: - token, text = get_fws(text) - ew.append(token) - continue - _3to2list5 = list(_wsp_splitter(text, 1)) - chars, remainder, = _3to2list5[:1] + [_3to2list5[1:]] - vtext = ValueTerminal(chars, 'vtext') - _validate_xtext(vtext) - ew.append(vtext) - text = ''.join(remainder) - return ew, value - -def get_unstructured(value): - """unstructured = (*([FWS] vchar) *WSP) / obs-unstruct - obs-unstruct = *((*LF *CR *(obs-utext) *LF *CR)) / FWS) - obs-utext = %d0 / obs-NO-WS-CTL / LF / CR - - obs-NO-WS-CTL is control characters except WSP/CR/LF. - - So, basically, we have printable runs, plus control characters or nulls in - the obsolete syntax, separated by whitespace. Since RFC 2047 uses the - obsolete syntax in its specification, but requires whitespace on either - side of the encoded words, I can see no reason to need to separate the - non-printable-non-whitespace from the printable runs if they occur, so we - parse this into xtext tokens separated by WSP tokens. - - Because an 'unstructured' value must by definition constitute the entire - value, this 'get' routine does not return a remaining value, only the - parsed TokenList. - - """ - # XXX: but what about bare CR and LF? They might signal the start or - # end of an encoded word. YAGNI for now, since out current parsers - # will never send us strings with bard CR or LF. - - unstructured = UnstructuredTokenList() - while value: - if value[0] in WSP: - token, value = get_fws(value) - unstructured.append(token) - continue - if value.startswith('=?'): - try: - token, value = get_encoded_word(value) - except errors.HeaderParseError: - pass - else: - have_ws = True - if len(unstructured) > 0: - if unstructured[-1].token_type != 'fws': - unstructured.defects.append(errors.InvalidHeaderDefect( - "missing whitespace before encoded word")) - have_ws = False - if have_ws and len(unstructured) > 1: - if unstructured[-2].token_type == 'encoded-word': - unstructured[-1] = EWWhiteSpaceTerminal( - unstructured[-1], 'fws') - unstructured.append(token) - continue - _3to2list7 = list(_wsp_splitter(value, 1)) - tok, remainder, = _3to2list7[:1] + [_3to2list7[1:]] - vtext = ValueTerminal(tok, 'vtext') - _validate_xtext(vtext) - unstructured.append(vtext) - value = ''.join(remainder) - return unstructured - -def get_qp_ctext(value): - """ctext = - - This is not the RFC ctext, since we are handling nested comments in comment - and unquoting quoted-pairs here. We allow anything except the '()' - characters, but if we find any ASCII other than the RFC defined printable - ASCII an NonPrintableDefect is added to the token's defects list. Since - quoted pairs are converted to their unquoted values, what is returned is - a 'ptext' token. In this case it is a WhiteSpaceTerminal, so it's value - is ' '. - - """ - ptext, value, _ = _get_ptext_to_endchars(value, '()') - ptext = WhiteSpaceTerminal(ptext, 'ptext') - _validate_xtext(ptext) - return ptext, value - -def get_qcontent(value): - """qcontent = qtext / quoted-pair - - We allow anything except the DQUOTE character, but if we find any ASCII - other than the RFC defined printable ASCII an NonPrintableDefect is - added to the token's defects list. Any quoted pairs are converted to their - unquoted values, so what is returned is a 'ptext' token. In this case it - is a ValueTerminal. - - """ - ptext, value, _ = _get_ptext_to_endchars(value, '"') - ptext = ValueTerminal(ptext, 'ptext') - _validate_xtext(ptext) - return ptext, value - -def get_atext(value): - """atext = - - We allow any non-ATOM_ENDS in atext, but add an InvalidATextDefect to - the token's defects list if we find non-atext characters. - """ - m = _non_atom_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected atext but found '{}'".format(value)) - atext = m.group() - value = value[len(atext):] - atext = ValueTerminal(atext, 'atext') - _validate_xtext(atext) - return atext, value - -def get_bare_quoted_string(value): - """bare-quoted-string = DQUOTE *([FWS] qcontent) [FWS] DQUOTE - - A quoted-string without the leading or trailing white space. Its - value is the text between the quote marks, with whitespace - preserved and quoted pairs decoded. - """ - if value[0] != '"': - raise errors.HeaderParseError( - "expected '\"' but found '{}'".format(value)) - bare_quoted_string = BareQuotedString() - value = value[1:] - while value and value[0] != '"': - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_qcontent(value) - bare_quoted_string.append(token) - if not value: - bare_quoted_string.defects.append(errors.InvalidHeaderDefect( - "end of header inside quoted string")) - return bare_quoted_string, value - return bare_quoted_string, value[1:] - -def get_comment(value): - """comment = "(" *([FWS] ccontent) [FWS] ")" - ccontent = ctext / quoted-pair / comment - - We handle nested comments here, and quoted-pair in our qp-ctext routine. - """ - if value and value[0] != '(': - raise errors.HeaderParseError( - "expected '(' but found '{}'".format(value)) - comment = Comment() - value = value[1:] - while value and value[0] != ")": - if value[0] in WSP: - token, value = get_fws(value) - elif value[0] == '(': - token, value = get_comment(value) - else: - token, value = get_qp_ctext(value) - comment.append(token) - if not value: - comment.defects.append(errors.InvalidHeaderDefect( - "end of header inside comment")) - return comment, value - return comment, value[1:] - -def get_cfws(value): - """CFWS = (1*([FWS] comment) [FWS]) / FWS - - """ - cfws = CFWSList() - while value and value[0] in CFWS_LEADER: - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_comment(value) - cfws.append(token) - return cfws, value - -def get_quoted_string(value): - """quoted-string = [CFWS] [CFWS] - - 'bare-quoted-string' is an intermediate class defined by this - parser and not by the RFC grammar. It is the quoted string - without any attached CFWS. - """ - quoted_string = QuotedString() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - quoted_string.append(token) - token, value = get_bare_quoted_string(value) - quoted_string.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - quoted_string.append(token) - return quoted_string, value - -def get_atom(value): - """atom = [CFWS] 1*atext [CFWS] - - """ - atom = Atom() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - atom.append(token) - if value and value[0] in ATOM_ENDS: - raise errors.HeaderParseError( - "expected atom but found '{}'".format(value)) - token, value = get_atext(value) - atom.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - atom.append(token) - return atom, value - -def get_dot_atom_text(value): - """ dot-text = 1*atext *("." 1*atext) - - """ - dot_atom_text = DotAtomText() - if not value or value[0] in ATOM_ENDS: - raise errors.HeaderParseError("expected atom at a start of " - "dot-atom-text but found '{}'".format(value)) - while value and value[0] not in ATOM_ENDS: - token, value = get_atext(value) - dot_atom_text.append(token) - if value and value[0] == '.': - dot_atom_text.append(DOT) - value = value[1:] - if dot_atom_text[-1] is DOT: - raise errors.HeaderParseError("expected atom at end of dot-atom-text " - "but found '{}'".format('.'+value)) - return dot_atom_text, value - -def get_dot_atom(value): - """ dot-atom = [CFWS] dot-atom-text [CFWS] - - """ - dot_atom = DotAtom() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - dot_atom.append(token) - token, value = get_dot_atom_text(value) - dot_atom.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - dot_atom.append(token) - return dot_atom, value - -def get_word(value): - """word = atom / quoted-string - - Either atom or quoted-string may start with CFWS. We have to peel off this - CFWS first to determine which type of word to parse. Afterward we splice - the leading CFWS, if any, into the parsed sub-token. - - If neither an atom or a quoted-string is found before the next special, a - HeaderParseError is raised. - - The token returned is either an Atom or a QuotedString, as appropriate. - This means the 'word' level of the formal grammar is not represented in the - parse tree; this is because having that extra layer when manipulating the - parse tree is more confusing than it is helpful. - - """ - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - else: - leader = None - if value[0]=='"': - token, value = get_quoted_string(value) - elif value[0] in SPECIALS: - raise errors.HeaderParseError("Expected 'atom' or 'quoted-string' " - "but found '{}'".format(value)) - else: - token, value = get_atom(value) - if leader is not None: - token[:0] = [leader] - return token, value - -def get_phrase(value): - """ phrase = 1*word / obs-phrase - obs-phrase = word *(word / "." / CFWS) - - This means a phrase can be a sequence of words, periods, and CFWS in any - order as long as it starts with at least one word. If anything other than - words is detected, an ObsoleteHeaderDefect is added to the token's defect - list. We also accept a phrase that starts with CFWS followed by a dot; - this is registered as an InvalidHeaderDefect, since it is not supported by - even the obsolete grammar. - - """ - phrase = Phrase() - try: - token, value = get_word(value) - phrase.append(token) - except errors.HeaderParseError: - phrase.defects.append(errors.InvalidHeaderDefect( - "phrase does not start with word")) - while value and value[0] not in PHRASE_ENDS: - if value[0]=='.': - phrase.append(DOT) - phrase.defects.append(errors.ObsoleteHeaderDefect( - "period in 'phrase'")) - value = value[1:] - else: - try: - token, value = get_word(value) - except errors.HeaderParseError: - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - phrase.defects.append(errors.ObsoleteHeaderDefect( - "comment found without atom")) - else: - raise - phrase.append(token) - return phrase, value - -def get_local_part(value): - """ local-part = dot-atom / quoted-string / obs-local-part - - """ - local_part = LocalPart() - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected local-part but found '{}'".format(value)) - try: - token, value = get_dot_atom(value) - except errors.HeaderParseError: - try: - token, value = get_word(value) - except errors.HeaderParseError: - if value[0] != '\\' and value[0] in PHRASE_ENDS: - raise - token = TokenList() - if leader is not None: - token[:0] = [leader] - local_part.append(token) - if value and (value[0]=='\\' or value[0] not in PHRASE_ENDS): - obs_local_part, value = get_obs_local_part(str(local_part) + value) - if obs_local_part.token_type == 'invalid-obs-local-part': - local_part.defects.append(errors.InvalidHeaderDefect( - "local-part is not dot-atom, quoted-string, or obs-local-part")) - else: - local_part.defects.append(errors.ObsoleteHeaderDefect( - "local-part is not a dot-atom (contains CFWS)")) - local_part[0] = obs_local_part - try: - local_part.value.encode('ascii') - except UnicodeEncodeError: - local_part.defects.append(errors.NonASCIILocalPartDefect( - "local-part contains non-ASCII characters)")) - return local_part, value - -def get_obs_local_part(value): - """ obs-local-part = word *("." word) - """ - obs_local_part = ObsLocalPart() - last_non_ws_was_dot = False - while value and (value[0]=='\\' or value[0] not in PHRASE_ENDS): - if value[0] == '.': - if last_non_ws_was_dot: - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "invalid repeated '.'")) - obs_local_part.append(DOT) - last_non_ws_was_dot = True - value = value[1:] - continue - elif value[0]=='\\': - obs_local_part.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "'\\' character outside of quoted-string/ccontent")) - last_non_ws_was_dot = False - continue - if obs_local_part and obs_local_part[-1].token_type != 'dot': - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "missing '.' between words")) - try: - token, value = get_word(value) - last_non_ws_was_dot = False - except errors.HeaderParseError: - if value[0] not in CFWS_LEADER: - raise - token, value = get_cfws(value) - obs_local_part.append(token) - if (obs_local_part[0].token_type == 'dot' or - obs_local_part[0].token_type=='cfws' and - obs_local_part[1].token_type=='dot'): - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "Invalid leading '.' in local part")) - if (obs_local_part[-1].token_type == 'dot' or - obs_local_part[-1].token_type=='cfws' and - obs_local_part[-2].token_type=='dot'): - obs_local_part.defects.append(errors.InvalidHeaderDefect( - "Invalid trailing '.' in local part")) - if obs_local_part.defects: - obs_local_part.token_type = 'invalid-obs-local-part' - return obs_local_part, value - -def get_dtext(value): - """ dtext = / obs-dtext - obs-dtext = obs-NO-WS-CTL / quoted-pair - - We allow anything except the excluded characters, but if we find any - ASCII other than the RFC defined printable ASCII an NonPrintableDefect is - added to the token's defects list. Quoted pairs are converted to their - unquoted values, so what is returned is a ptext token, in this case a - ValueTerminal. If there were quoted-printables, an ObsoleteHeaderDefect is - added to the returned token's defect list. - - """ - ptext, value, had_qp = _get_ptext_to_endchars(value, '[]') - ptext = ValueTerminal(ptext, 'ptext') - if had_qp: - ptext.defects.append(errors.ObsoleteHeaderDefect( - "quoted printable found in domain-literal")) - _validate_xtext(ptext) - return ptext, value - -def _check_for_early_dl_end(value, domain_literal): - if value: - return False - domain_literal.append(errors.InvalidHeaderDefect( - "end of input inside domain-literal")) - domain_literal.append(ValueTerminal(']', 'domain-literal-end')) - return True - -def get_domain_literal(value): - """ domain-literal = [CFWS] "[" *([FWS] dtext) [FWS] "]" [CFWS] - - """ - domain_literal = DomainLiteral() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - domain_literal.append(token) - if not value: - raise errors.HeaderParseError("expected domain-literal") - if value[0] != '[': - raise errors.HeaderParseError("expected '[' at start of domain-literal " - "but found '{}'".format(value)) - value = value[1:] - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - domain_literal.append(ValueTerminal('[', 'domain-literal-start')) - if value[0] in WSP: - token, value = get_fws(value) - domain_literal.append(token) - token, value = get_dtext(value) - domain_literal.append(token) - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - if value[0] in WSP: - token, value = get_fws(value) - domain_literal.append(token) - if _check_for_early_dl_end(value, domain_literal): - return domain_literal, value - if value[0] != ']': - raise errors.HeaderParseError("expected ']' at end of domain-literal " - "but found '{}'".format(value)) - domain_literal.append(ValueTerminal(']', 'domain-literal-end')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - domain_literal.append(token) - return domain_literal, value - -def get_domain(value): - """ domain = dot-atom / domain-literal / obs-domain - obs-domain = atom *("." atom)) - - """ - domain = Domain() - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected domain but found '{}'".format(value)) - if value[0] == '[': - token, value = get_domain_literal(value) - if leader is not None: - token[:0] = [leader] - domain.append(token) - return domain, value - try: - token, value = get_dot_atom(value) - except errors.HeaderParseError: - token, value = get_atom(value) - if leader is not None: - token[:0] = [leader] - domain.append(token) - if value and value[0] == '.': - domain.defects.append(errors.ObsoleteHeaderDefect( - "domain is not a dot-atom (contains CFWS)")) - if domain[0].token_type == 'dot-atom': - domain[:] = domain[0] - while value and value[0] == '.': - domain.append(DOT) - token, value = get_atom(value[1:]) - domain.append(token) - return domain, value - -def get_addr_spec(value): - """ addr-spec = local-part "@" domain - - """ - addr_spec = AddrSpec() - token, value = get_local_part(value) - addr_spec.append(token) - if not value or value[0] != '@': - addr_spec.defects.append(errors.InvalidHeaderDefect( - "add-spec local part with no domain")) - return addr_spec, value - addr_spec.append(ValueTerminal('@', 'address-at-symbol')) - token, value = get_domain(value[1:]) - addr_spec.append(token) - return addr_spec, value - -def get_obs_route(value): - """ obs-route = obs-domain-list ":" - obs-domain-list = *(CFWS / ",") "@" domain *("," [CFWS] ["@" domain]) - - Returns an obs-route token with the appropriate sub-tokens (that is, - there is no obs-domain-list in the parse tree). - """ - obs_route = ObsRoute() - while value and (value[0]==',' or value[0] in CFWS_LEADER): - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - obs_route.append(token) - elif value[0] == ',': - obs_route.append(ListSeparator) - value = value[1:] - if not value or value[0] != '@': - raise errors.HeaderParseError( - "expected obs-route domain but found '{}'".format(value)) - obs_route.append(RouteComponentMarker) - token, value = get_domain(value[1:]) - obs_route.append(token) - while value and value[0]==',': - obs_route.append(ListSeparator) - value = value[1:] - if not value: - break - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - obs_route.append(token) - if value[0] == '@': - obs_route.append(RouteComponentMarker) - token, value = get_domain(value[1:]) - obs_route.append(token) - if not value: - raise errors.HeaderParseError("end of header while parsing obs-route") - if value[0] != ':': - raise errors.HeaderParseError( "expected ':' marking end of " - "obs-route but found '{}'".format(value)) - obs_route.append(ValueTerminal(':', 'end-of-obs-route-marker')) - return obs_route, value[1:] - -def get_angle_addr(value): - """ angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr - obs-angle-addr = [CFWS] "<" obs-route addr-spec ">" [CFWS] - - """ - angle_addr = AngleAddr() - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - angle_addr.append(token) - if not value or value[0] != '<': - raise errors.HeaderParseError( - "expected angle-addr but found '{}'".format(value)) - angle_addr.append(ValueTerminal('<', 'angle-addr-start')) - value = value[1:] - # Although it is not legal per RFC5322, SMTP uses '<>' in certain - # circumstances. - if value[0] == '>': - angle_addr.append(ValueTerminal('>', 'angle-addr-end')) - angle_addr.defects.append(errors.InvalidHeaderDefect( - "null addr-spec in angle-addr")) - value = value[1:] - return angle_addr, value - try: - token, value = get_addr_spec(value) - except errors.HeaderParseError: - try: - token, value = get_obs_route(value) - angle_addr.defects.append(errors.ObsoleteHeaderDefect( - "obsolete route specification in angle-addr")) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected addr-spec or obs-route but found '{}'".format(value)) - angle_addr.append(token) - token, value = get_addr_spec(value) - angle_addr.append(token) - if value and value[0] == '>': - value = value[1:] - else: - angle_addr.defects.append(errors.InvalidHeaderDefect( - "missing trailing '>' on angle-addr")) - angle_addr.append(ValueTerminal('>', 'angle-addr-end')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - angle_addr.append(token) - return angle_addr, value - -def get_display_name(value): - """ display-name = phrase - - Because this is simply a name-rule, we don't return a display-name - token containing a phrase, but rather a display-name token with - the content of the phrase. - - """ - display_name = DisplayName() - token, value = get_phrase(value) - display_name.extend(token[:]) - display_name.defects = token.defects[:] - return display_name, value - - -def get_name_addr(value): - """ name-addr = [display-name] angle-addr - - """ - name_addr = NameAddr() - # Both the optional display name and the angle-addr can start with cfws. - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(leader)) - if value[0] != '<': - if value[0] in PHRASE_ENDS: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(value)) - token, value = get_display_name(value) - if not value: - raise errors.HeaderParseError( - "expected name-addr but found '{}'".format(token)) - if leader is not None: - token[0][:0] = [leader] - leader = None - name_addr.append(token) - token, value = get_angle_addr(value) - if leader is not None: - token[:0] = [leader] - name_addr.append(token) - return name_addr, value - -def get_mailbox(value): - """ mailbox = name-addr / addr-spec - - """ - # The only way to figure out if we are dealing with a name-addr or an - # addr-spec is to try parsing each one. - mailbox = Mailbox() - try: - token, value = get_name_addr(value) - except errors.HeaderParseError: - try: - token, value = get_addr_spec(value) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected mailbox but found '{}'".format(value)) - if any(isinstance(x, errors.InvalidHeaderDefect) - for x in token.all_defects): - mailbox.token_type = 'invalid-mailbox' - mailbox.append(token) - return mailbox, value - -def get_invalid_mailbox(value, endchars): - """ Read everything up to one of the chars in endchars. - - This is outside the formal grammar. The InvalidMailbox TokenList that is - returned acts like a Mailbox, but the data attributes are None. - - """ - invalid_mailbox = InvalidMailbox() - while value and value[0] not in endchars: - if value[0] in PHRASE_ENDS: - invalid_mailbox.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - invalid_mailbox.append(token) - return invalid_mailbox, value - -def get_mailbox_list(value): - """ mailbox-list = (mailbox *("," mailbox)) / obs-mbox-list - obs-mbox-list = *([CFWS] ",") mailbox *("," [mailbox / CFWS]) - - For this routine we go outside the formal grammar in order to improve error - handling. We recognize the end of the mailbox list only at the end of the - value or at a ';' (the group terminator). This is so that we can turn - invalid mailboxes into InvalidMailbox tokens and continue parsing any - remaining valid mailboxes. We also allow all mailbox entries to be null, - and this condition is handled appropriately at a higher level. - - """ - mailbox_list = MailboxList() - while value and value[0] != ';': - try: - token, value = get_mailbox(value) - mailbox_list.append(token) - except errors.HeaderParseError: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value or value[0] in ',;': - mailbox_list.append(leader) - mailbox_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in mailbox-list")) - else: - token, value = get_invalid_mailbox(value, ',;') - if leader is not None: - token[:0] = [leader] - mailbox_list.append(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - elif value[0] == ',': - mailbox_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in mailbox-list")) - else: - token, value = get_invalid_mailbox(value, ',;') - if leader is not None: - token[:0] = [leader] - mailbox_list.append(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - if value and value[0] not in ',;': - # Crap after mailbox; treat it as an invalid mailbox. - # The mailbox info will still be available. - mailbox = mailbox_list[-1] - mailbox.token_type = 'invalid-mailbox' - token, value = get_invalid_mailbox(value, ',;') - mailbox.extend(token) - mailbox_list.defects.append(errors.InvalidHeaderDefect( - "invalid mailbox in mailbox-list")) - if value and value[0] == ',': - mailbox_list.append(ListSeparator) - value = value[1:] - return mailbox_list, value - - -def get_group_list(value): - """ group-list = mailbox-list / CFWS / obs-group-list - obs-group-list = 1*([CFWS] ",") [CFWS] - - """ - group_list = GroupList() - if not value: - group_list.defects.append(errors.InvalidHeaderDefect( - "end of header before group-list")) - return group_list, value - leader = None - if value and value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - # This should never happen in email parsing, since CFWS-only is a - # legal alternative to group-list in a group, which is the only - # place group-list appears. - group_list.defects.append(errors.InvalidHeaderDefect( - "end of header in group-list")) - group_list.append(leader) - return group_list, value - if value[0] == ';': - group_list.append(leader) - return group_list, value - token, value = get_mailbox_list(value) - if len(token.all_mailboxes)==0: - if leader is not None: - group_list.append(leader) - group_list.extend(token) - group_list.defects.append(errors.ObsoleteHeaderDefect( - "group-list with empty entries")) - return group_list, value - if leader is not None: - token[:0] = [leader] - group_list.append(token) - return group_list, value - -def get_group(value): - """ group = display-name ":" [group-list] ";" [CFWS] - - """ - group = Group() - token, value = get_display_name(value) - if not value or value[0] != ':': - raise errors.HeaderParseError("expected ':' at end of group " - "display name but found '{}'".format(value)) - group.append(token) - group.append(ValueTerminal(':', 'group-display-name-terminator')) - value = value[1:] - if value and value[0] == ';': - group.append(ValueTerminal(';', 'group-terminator')) - return group, value[1:] - token, value = get_group_list(value) - group.append(token) - if not value: - group.defects.append(errors.InvalidHeaderDefect( - "end of header in group")) - if value[0] != ';': - raise errors.HeaderParseError( - "expected ';' at end of group but found {}".format(value)) - group.append(ValueTerminal(';', 'group-terminator')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - group.append(token) - return group, value - -def get_address(value): - """ address = mailbox / group - - Note that counter-intuitively, an address can be either a single address or - a list of addresses (a group). This is why the returned Address object has - a 'mailboxes' attribute which treats a single address as a list of length - one. When you need to differentiate between to two cases, extract the single - element, which is either a mailbox or a group token. - - """ - # The formal grammar isn't very helpful when parsing an address. mailbox - # and group, especially when allowing for obsolete forms, start off very - # similarly. It is only when you reach one of @, <, or : that you know - # what you've got. So, we try each one in turn, starting with the more - # likely of the two. We could perhaps make this more efficient by looking - # for a phrase and then branching based on the next character, but that - # would be a premature optimization. - address = Address() - try: - token, value = get_group(value) - except errors.HeaderParseError: - try: - token, value = get_mailbox(value) - except errors.HeaderParseError: - raise errors.HeaderParseError( - "expected address but found '{}'".format(value)) - address.append(token) - return address, value - -def get_address_list(value): - """ address_list = (address *("," address)) / obs-addr-list - obs-addr-list = *([CFWS] ",") address *("," [address / CFWS]) - - We depart from the formal grammar here by continuing to parse until the end - of the input, assuming the input to be entirely composed of an - address-list. This is always true in email parsing, and allows us - to skip invalid addresses to parse additional valid ones. - - """ - address_list = AddressList() - while value: - try: - token, value = get_address(value) - address_list.append(token) - except errors.HeaderParseError as err: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value or value[0] == ',': - address_list.append(leader) - address_list.defects.append(errors.ObsoleteHeaderDefect( - "address-list entry with no content")) - else: - token, value = get_invalid_mailbox(value, ',') - if leader is not None: - token[:0] = [leader] - address_list.append(Address([token])) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - elif value[0] == ',': - address_list.defects.append(errors.ObsoleteHeaderDefect( - "empty element in address-list")) - else: - token, value = get_invalid_mailbox(value, ',') - if leader is not None: - token[:0] = [leader] - address_list.append(Address([token])) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - if value and value[0] != ',': - # Crap after address; treat it as an invalid mailbox. - # The mailbox info will still be available. - mailbox = address_list[-1][0] - mailbox.token_type = 'invalid-mailbox' - token, value = get_invalid_mailbox(value, ',') - mailbox.extend(token) - address_list.defects.append(errors.InvalidHeaderDefect( - "invalid address in address-list")) - if value: # Must be a , at this point. - address_list.append(ValueTerminal(',', 'list-separator')) - value = value[1:] - return address_list, value - -# -# XXX: As I begin to add additional header parsers, I'm realizing we probably -# have two level of parser routines: the get_XXX methods that get a token in -# the grammar, and parse_XXX methods that parse an entire field value. So -# get_address_list above should really be a parse_ method, as probably should -# be get_unstructured. -# - -def parse_mime_version(value): - """ mime-version = [CFWS] 1*digit [CFWS] "." [CFWS] 1*digit [CFWS] - - """ - # The [CFWS] is implicit in the RFC 2045 BNF. - # XXX: This routine is a bit verbose, should factor out a get_int method. - mime_version = MIMEVersion() - if not value: - mime_version.defects.append(errors.HeaderMissingRequiredValue( - "Missing MIME version number (eg: 1.0)")) - return mime_version - if value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value: - mime_version.defects.append(errors.HeaderMissingRequiredValue( - "Expected MIME version number but found only CFWS")) - digits = '' - while value and value[0] != '.' and value[0] not in CFWS_LEADER: - digits += value[0] - value = value[1:] - if not digits.isdigit(): - mime_version.defects.append(errors.InvalidHeaderDefect( - "Expected MIME major version number but found {!r}".format(digits))) - mime_version.append(ValueTerminal(digits, 'xtext')) - else: - mime_version.major = int(digits) - mime_version.append(ValueTerminal(digits, 'digits')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value or value[0] != '.': - if mime_version.major is not None: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Incomplete MIME version; found only major number")) - if value: - mime_version.append(ValueTerminal(value, 'xtext')) - return mime_version - mime_version.append(ValueTerminal('.', 'version-separator')) - value = value[1:] - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if not value: - if mime_version.major is not None: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Incomplete MIME version; found only major number")) - return mime_version - digits = '' - while value and value[0] not in CFWS_LEADER: - digits += value[0] - value = value[1:] - if not digits.isdigit(): - mime_version.defects.append(errors.InvalidHeaderDefect( - "Expected MIME minor version number but found {!r}".format(digits))) - mime_version.append(ValueTerminal(digits, 'xtext')) - else: - mime_version.minor = int(digits) - mime_version.append(ValueTerminal(digits, 'digits')) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mime_version.append(token) - if value: - mime_version.defects.append(errors.InvalidHeaderDefect( - "Excess non-CFWS text after MIME version")) - mime_version.append(ValueTerminal(value, 'xtext')) - return mime_version - -def get_invalid_parameter(value): - """ Read everything up to the next ';'. - - This is outside the formal grammar. The InvalidParameter TokenList that is - returned acts like a Parameter, but the data attributes are None. - - """ - invalid_parameter = InvalidParameter() - while value and value[0] != ';': - if value[0] in PHRASE_ENDS: - invalid_parameter.append(ValueTerminal(value[0], - 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - invalid_parameter.append(token) - return invalid_parameter, value - -def get_ttext(value): - """ttext = - - We allow any non-TOKEN_ENDS in ttext, but add defects to the token's - defects list if we find non-ttext characters. We also register defects for - *any* non-printables even though the RFC doesn't exclude all of them, - because we follow the spirit of RFC 5322. - - """ - m = _non_token_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected ttext but found '{}'".format(value)) - ttext = m.group() - value = value[len(ttext):] - ttext = ValueTerminal(ttext, 'ttext') - _validate_xtext(ttext) - return ttext, value - -def get_token(value): - """token = [CFWS] 1*ttext [CFWS] - - The RFC equivalent of ttext is any US-ASCII chars except space, ctls, or - tspecials. We also exclude tabs even though the RFC doesn't. - - The RFC implies the CFWS but is not explicit about it in the BNF. - - """ - mtoken = Token() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mtoken.append(token) - if value and value[0] in TOKEN_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_ttext(value) - mtoken.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - mtoken.append(token) - return mtoken, value - -def get_attrtext(value): - """attrtext = 1*(any non-ATTRIBUTE_ENDS character) - - We allow any non-ATTRIBUTE_ENDS in attrtext, but add defects to the - token's defects list if we find non-attrtext characters. We also register - defects for *any* non-printables even though the RFC doesn't exclude all of - them, because we follow the spirit of RFC 5322. - - """ - m = _non_attribute_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected attrtext but found {!r}".format(value)) - attrtext = m.group() - value = value[len(attrtext):] - attrtext = ValueTerminal(attrtext, 'attrtext') - _validate_xtext(attrtext) - return attrtext, value - -def get_attribute(value): - """ [CFWS] 1*attrtext [CFWS] - - This version of the BNF makes the CFWS explicit, and as usual we use a - value terminal for the actual run of characters. The RFC equivalent of - attrtext is the token characters, with the subtraction of '*', "'", and '%'. - We include tab in the excluded set just as we do for token. - - """ - attribute = Attribute() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - if value and value[0] in ATTRIBUTE_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_attrtext(value) - attribute.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - return attribute, value - -def get_extended_attrtext(value): - """attrtext = 1*(any non-ATTRIBUTE_ENDS character plus '%') - - This is a special parsing routine so that we get a value that - includes % escapes as a single string (which we decode as a single - string later). - - """ - m = _non_extended_attribute_end_matcher(value) - if not m: - raise errors.HeaderParseError( - "expected extended attrtext but found {!r}".format(value)) - attrtext = m.group() - value = value[len(attrtext):] - attrtext = ValueTerminal(attrtext, 'extended-attrtext') - _validate_xtext(attrtext) - return attrtext, value - -def get_extended_attribute(value): - """ [CFWS] 1*extended_attrtext [CFWS] - - This is like the non-extended version except we allow % characters, so that - we can pick up an encoded value as a single string. - - """ - # XXX: should we have an ExtendedAttribute TokenList? - attribute = Attribute() - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - if value and value[0] in EXTENDED_ATTRIBUTE_ENDS: - raise errors.HeaderParseError( - "expected token but found '{}'".format(value)) - token, value = get_extended_attrtext(value) - attribute.append(token) - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - attribute.append(token) - return attribute, value - -def get_section(value): - """ '*' digits - - The formal BNF is more complicated because leading 0s are not allowed. We - check for that and add a defect. We also assume no CFWS is allowed between - the '*' and the digits, though the RFC is not crystal clear on that. - The caller should already have dealt with leading CFWS. - - """ - section = Section() - if not value or value[0] != '*': - raise errors.HeaderParseError("Expected section but found {}".format( - value)) - section.append(ValueTerminal('*', 'section-marker')) - value = value[1:] - if not value or not value[0].isdigit(): - raise errors.HeaderParseError("Expected section number but " - "found {}".format(value)) - digits = '' - while value and value[0].isdigit(): - digits += value[0] - value = value[1:] - if digits[0] == '0' and digits != '0': - section.defects.append(errors.InvalidHeaderError("section number" - "has an invalid leading 0")) - section.number = int(digits) - section.append(ValueTerminal(digits, 'digits')) - return section, value - - -def get_value(value): - """ quoted-string / attribute - - """ - v = Value() - if not value: - raise errors.HeaderParseError("Expected value but found end of string") - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - raise errors.HeaderParseError("Expected value but found " - "only {}".format(leader)) - if value[0] == '"': - token, value = get_quoted_string(value) - else: - token, value = get_extended_attribute(value) - if leader is not None: - token[:0] = [leader] - v.append(token) - return v, value - -def get_parameter(value): - """ attribute [section] ["*"] [CFWS] "=" value - - The CFWS is implied by the RFC but not made explicit in the BNF. This - simplified form of the BNF from the RFC is made to conform with the RFC BNF - through some extra checks. We do it this way because it makes both error - recovery and working with the resulting parse tree easier. - """ - # It is possible CFWS would also be implicitly allowed between the section - # and the 'extended-attribute' marker (the '*') , but we've never seen that - # in the wild and we will therefore ignore the possibility. - param = Parameter() - token, value = get_attribute(value) - param.append(token) - if not value or value[0] == ';': - param.defects.append(errors.InvalidHeaderDefect("Parameter contains " - "name ({}) but no value".format(token))) - return param, value - if value[0] == '*': - try: - token, value = get_section(value) - param.sectioned = True - param.append(token) - except errors.HeaderParseError: - pass - if not value: - raise errors.HeaderParseError("Incomplete parameter") - if value[0] == '*': - param.append(ValueTerminal('*', 'extended-parameter-marker')) - value = value[1:] - param.extended = True - if value[0] != '=': - raise errors.HeaderParseError("Parameter not followed by '='") - param.append(ValueTerminal('=', 'parameter-separator')) - value = value[1:] - leader = None - if value and value[0] in CFWS_LEADER: - token, value = get_cfws(value) - param.append(token) - remainder = None - appendto = param - if param.extended and value and value[0] == '"': - # Now for some serious hackery to handle the common invalid case of - # double quotes around an extended value. We also accept (with defect) - # a value marked as encoded that isn't really. - qstring, remainder = get_quoted_string(value) - inner_value = qstring.stripped_value - semi_valid = False - if param.section_number == 0: - if inner_value and inner_value[0] == "'": - semi_valid = True - else: - token, rest = get_attrtext(inner_value) - if rest and rest[0] == "'": - semi_valid = True - else: - try: - token, rest = get_extended_attrtext(inner_value) - except: - pass - else: - if not rest: - semi_valid = True - if semi_valid: - param.defects.append(errors.InvalidHeaderDefect( - "Quoted string value for extended parameter is invalid")) - param.append(qstring) - for t in qstring: - if t.token_type == 'bare-quoted-string': - t[:] = [] - appendto = t - break - value = inner_value - else: - remainder = None - param.defects.append(errors.InvalidHeaderDefect( - "Parameter marked as extended but appears to have a " - "quoted string value that is non-encoded")) - if value and value[0] == "'": - token = None - else: - token, value = get_value(value) - if not param.extended or param.section_number > 0: - if not value or value[0] != "'": - appendto.append(token) - if remainder is not None: - assert not value, value - value = remainder - return param, value - param.defects.append(errors.InvalidHeaderDefect( - "Apparent initial-extended-value but attribute " - "was not marked as extended or was not initial section")) - if not value: - # Assume the charset/lang is missing and the token is the value. - param.defects.append(errors.InvalidHeaderDefect( - "Missing required charset/lang delimiters")) - appendto.append(token) - if remainder is None: - return param, value - else: - if token is not None: - for t in token: - if t.token_type == 'extended-attrtext': - break - t.token_type == 'attrtext' - appendto.append(t) - param.charset = t.value - if value[0] != "'": - raise errors.HeaderParseError("Expected RFC2231 char/lang encoding " - "delimiter, but found {!r}".format(value)) - appendto.append(ValueTerminal("'", 'RFC2231 delimiter')) - value = value[1:] - if value and value[0] != "'": - token, value = get_attrtext(value) - appendto.append(token) - param.lang = token.value - if not value or value[0] != "'": - raise errors.HeaderParseError("Expected RFC2231 char/lang encoding " - "delimiter, but found {}".format(value)) - appendto.append(ValueTerminal("'", 'RFC2231 delimiter')) - value = value[1:] - if remainder is not None: - # Treat the rest of value as bare quoted string content. - v = Value() - while value: - if value[0] in WSP: - token, value = get_fws(value) - else: - token, value = get_qcontent(value) - v.append(token) - token = v - else: - token, value = get_value(value) - appendto.append(token) - if remainder is not None: - assert not value, value - value = remainder - return param, value - -def parse_mime_parameters(value): - """ parameter *( ";" parameter ) - - That BNF is meant to indicate this routine should only be called after - finding and handling the leading ';'. There is no corresponding rule in - the formal RFC grammar, but it is more convenient for us for the set of - parameters to be treated as its own TokenList. - - This is 'parse' routine because it consumes the reminaing value, but it - would never be called to parse a full header. Instead it is called to - parse everything after the non-parameter value of a specific MIME header. - - """ - mime_parameters = MimeParameters() - while value: - try: - token, value = get_parameter(value) - mime_parameters.append(token) - except errors.HeaderParseError as err: - leader = None - if value[0] in CFWS_LEADER: - leader, value = get_cfws(value) - if not value: - mime_parameters.append(leader) - return mime_parameters - if value[0] == ';': - if leader is not None: - mime_parameters.append(leader) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "parameter entry with no content")) - else: - token, value = get_invalid_parameter(value) - if leader: - token[:0] = [leader] - mime_parameters.append(token) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "invalid parameter {!r}".format(token))) - if value and value[0] != ';': - # Junk after the otherwise valid parameter. Mark it as - # invalid, but it will have a value. - param = mime_parameters[-1] - param.token_type = 'invalid-parameter' - token, value = get_invalid_parameter(value) - param.extend(token) - mime_parameters.defects.append(errors.InvalidHeaderDefect( - "parameter with invalid trailing text {!r}".format(token))) - if value: - # Must be a ';' at this point. - mime_parameters.append(ValueTerminal(';', 'parameter-separator')) - value = value[1:] - return mime_parameters - -def _find_mime_parameters(tokenlist, value): - """Do our best to find the parameters in an invalid MIME header - - """ - while value and value[0] != ';': - if value[0] in PHRASE_ENDS: - tokenlist.append(ValueTerminal(value[0], 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - tokenlist.append(token) - if not value: - return - tokenlist.append(ValueTerminal(';', 'parameter-separator')) - tokenlist.append(parse_mime_parameters(value[1:])) - -def parse_content_type_header(value): - """ maintype "/" subtype *( ";" parameter ) - - The maintype and substype are tokens. Theoretically they could - be checked against the official IANA list + x-token, but we - don't do that. - """ - ctype = ContentType() - recover = False - if not value: - ctype.defects.append(errors.HeaderMissingRequiredValue( - "Missing content type specification")) - return ctype - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content maintype but found {!r}".format(value))) - _find_mime_parameters(ctype, value) - return ctype - ctype.append(token) - # XXX: If we really want to follow the formal grammer we should make - # mantype and subtype specialized TokenLists here. Probably not worth it. - if not value or value[0] != '/': - ctype.defects.append(errors.InvalidHeaderDefect( - "Invalid content type")) - if value: - _find_mime_parameters(ctype, value) - return ctype - ctype.maintype = token.value.strip().lower() - ctype.append(ValueTerminal('/', 'content-type-separator')) - value = value[1:] - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content subtype but found {!r}".format(value))) - _find_mime_parameters(ctype, value) - return ctype - ctype.append(token) - ctype.subtype = token.value.strip().lower() - if not value: - return ctype - if value[0] != ';': - ctype.defects.append(errors.InvalidHeaderDefect( - "Only parameters are valid after content type, but " - "found {!r}".format(value))) - # The RFC requires that a syntactically invalid content-type be treated - # as text/plain. Perhaps we should postel this, but we should probably - # only do that if we were checking the subtype value against IANA. - del ctype.maintype, ctype.subtype - _find_mime_parameters(ctype, value) - return ctype - ctype.append(ValueTerminal(';', 'parameter-separator')) - ctype.append(parse_mime_parameters(value[1:])) - return ctype - -def parse_content_disposition_header(value): - """ disposition-type *( ";" parameter ) - - """ - disp_header = ContentDisposition() - if not value: - disp_header.defects.append(errors.HeaderMissingRequiredValue( - "Missing content disposition")) - return disp_header - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content disposition but found {!r}".format(value))) - _find_mime_parameters(disp_header, value) - return disp_header - disp_header.append(token) - disp_header.content_disposition = token.value.strip().lower() - if not value: - return disp_header - if value[0] != ';': - disp_header.defects.append(errors.InvalidHeaderDefect( - "Only parameters are valid after content disposition, but " - "found {!r}".format(value))) - _find_mime_parameters(disp_header, value) - return disp_header - disp_header.append(ValueTerminal(';', 'parameter-separator')) - disp_header.append(parse_mime_parameters(value[1:])) - return disp_header - -def parse_content_transfer_encoding_header(value): - """ mechanism - - """ - # We should probably validate the values, since the list is fixed. - cte_header = ContentTransferEncoding() - if not value: - cte_header.defects.append(errors.HeaderMissingRequiredValue( - "Missing content transfer encoding")) - return cte_header - try: - token, value = get_token(value) - except errors.HeaderParseError: - ctype.defects.append(errors.InvalidHeaderDefect( - "Expected content trnasfer encoding but found {!r}".format(value))) - else: - cte_header.append(token) - cte_header.cte = token.value.strip().lower() - if not value: - return cte_header - while value: - cte_header.defects.append(errors.InvalidHeaderDefect( - "Extra text after content transfer encoding")) - if value[0] in PHRASE_ENDS: - cte_header.append(ValueTerminal(value[0], 'misplaced-special')) - value = value[1:] - else: - token, value = get_phrase(value) - cte_header.append(token) - return cte_header diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -""" -Backport of the Python 3.3 email package for Python-Future. - -A package for parsing, handling, and generating email messages. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -# Install the surrogate escape handler here because this is used by many -# modules in the email package. -from future.utils import surrogateescape -surrogateescape.register_surrogateescape() -# (Should this be done globally by ``future``?) - - -__version__ = '5.1.0' - -__all__ = [ - 'base64mime', - 'charset', - 'encoders', - 'errors', - 'feedparser', - 'generator', - 'header', - 'iterators', - 'message', - 'message_from_file', - 'message_from_binary_file', - 'message_from_string', - 'message_from_bytes', - 'mime', - 'parser', - 'quoprimime', - 'utils', - ] - - - -# Some convenience routines. Don't import Parser and Message as side-effects -# of importing email since those cascadingly import most of the rest of the -# email package. -def message_from_string(s, *args, **kws): - """Parse a string into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import Parser - return Parser(*args, **kws).parsestr(s) - -def message_from_bytes(s, *args, **kws): - """Parse a bytes string into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import BytesParser - return BytesParser(*args, **kws).parsebytes(s) - -def message_from_file(fp, *args, **kws): - """Read a file and parse its contents into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import Parser - return Parser(*args, **kws).parse(fp) - -def message_from_binary_file(fp, *args, **kws): - """Read a binary file and parse its contents into a Message object model. - - Optional _class and strict are passed to the Parser constructor. - """ - from future.backports.email.parser import BytesParser - return BytesParser(*args, **kws).parse(fp) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/iterators.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/iterators.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/iterators.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/iterators.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Various types of useful iterators and generators.""" -from __future__ import print_function -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = [ - 'body_line_iterator', - 'typed_subpart_iterator', - 'walk', - # Do not include _structure() since it's part of the debugging API. - ] - -import sys -from io import StringIO - - -# This function will become a method of the Message class -def walk(self): - """Walk over the message tree, yielding each subpart. - - The walk is performed in depth-first order. This method is a - generator. - """ - yield self - if self.is_multipart(): - for subpart in self.get_payload(): - for subsubpart in subpart.walk(): - yield subsubpart - - -# These two functions are imported into the Iterators.py interface module. -def body_line_iterator(msg, decode=False): - """Iterate over the parts, returning string payloads line-by-line. - - Optional decode (default False) is passed through to .get_payload(). - """ - for subpart in msg.walk(): - payload = subpart.get_payload(decode=decode) - if isinstance(payload, str): - for line in StringIO(payload): - yield line - - -def typed_subpart_iterator(msg, maintype='text', subtype=None): - """Iterate over the subparts with a given MIME type. - - Use `maintype' as the main MIME type to match against; this defaults to - "text". Optional `subtype' is the MIME subtype to match against; if - omitted, only the main type is matched. - """ - for subpart in msg.walk(): - if subpart.get_content_maintype() == maintype: - if subtype is None or subpart.get_content_subtype() == subtype: - yield subpart - - -def _structure(msg, fp=None, level=0, include_default=False): - """A handy debugging aid""" - if fp is None: - fp = sys.stdout - tab = ' ' * (level * 4) - print(tab + msg.get_content_type(), end='', file=fp) - if include_default: - print(' [%s]' % msg.get_default_type(), file=fp) - else: - print(file=fp) - if msg.is_multipart(): - for subpart in msg.get_payload(): - _structure(subpart, fp, level+1, include_default) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/message.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/message.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/message.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/message.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,882 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Basic message object for the email package object model.""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import list, range, str, zip - -__all__ = ['Message'] - -import re -import uu -import base64 -import binascii -from io import BytesIO, StringIO - -# Intrapackage imports -from future.utils import as_native_str -from future.backports.email import utils -from future.backports.email import errors -from future.backports.email._policybase import compat32 -from future.backports.email import charset as _charset -from future.backports.email._encoded_words import decode_b -Charset = _charset.Charset - -SEMISPACE = '; ' - -# Regular expression that matches `special' characters in parameters, the -# existence of which force quoting of the parameter value. -tspecials = re.compile(r'[ \(\)<>@,;:\\"/\[\]\?=]') - - -def _splitparam(param): - # Split header parameters. BAW: this may be too simple. It isn't - # strictly RFC 2045 (section 5.1) compliant, but it catches most headers - # found in the wild. We may eventually need a full fledged parser. - # RDM: we might have a Header here; for now just stringify it. - a, sep, b = str(param).partition(';') - if not sep: - return a.strip(), None - return a.strip(), b.strip() - -def _formatparam(param, value=None, quote=True): - """Convenience function to format and return a key=value pair. - - This will quote the value if needed or if quote is true. If value is a - three tuple (charset, language, value), it will be encoded according - to RFC2231 rules. If it contains non-ascii characters it will likewise - be encoded according to RFC2231 rules, using the utf-8 charset and - a null language. - """ - if value is not None and len(value) > 0: - # A tuple is used for RFC 2231 encoded parameter values where items - # are (charset, language, value). charset is a string, not a Charset - # instance. RFC 2231 encoded values are never quoted, per RFC. - if isinstance(value, tuple): - # Encode as per RFC 2231 - param += '*' - value = utils.encode_rfc2231(value[2], value[0], value[1]) - return '%s=%s' % (param, value) - else: - try: - value.encode('ascii') - except UnicodeEncodeError: - param += '*' - value = utils.encode_rfc2231(value, 'utf-8', '') - return '%s=%s' % (param, value) - # BAW: Please check this. I think that if quote is set it should - # force quoting even if not necessary. - if quote or tspecials.search(value): - return '%s="%s"' % (param, utils.quote(value)) - else: - return '%s=%s' % (param, value) - else: - return param - -def _parseparam(s): - # RDM This might be a Header, so for now stringify it. - s = ';' + str(s) - plist = [] - while s[:1] == ';': - s = s[1:] - end = s.find(';') - while end > 0 and (s.count('"', 0, end) - s.count('\\"', 0, end)) % 2: - end = s.find(';', end + 1) - if end < 0: - end = len(s) - f = s[:end] - if '=' in f: - i = f.index('=') - f = f[:i].strip().lower() + '=' + f[i+1:].strip() - plist.append(f.strip()) - s = s[end:] - return plist - - -def _unquotevalue(value): - # This is different than utils.collapse_rfc2231_value() because it doesn't - # try to convert the value to a unicode. Message.get_param() and - # Message.get_params() are both currently defined to return the tuple in - # the face of RFC 2231 parameters. - if isinstance(value, tuple): - return value[0], value[1], utils.unquote(value[2]) - else: - return utils.unquote(value) - - -class Message(object): - """Basic message object. - - A message object is defined as something that has a bunch of RFC 2822 - headers and a payload. It may optionally have an envelope header - (a.k.a. Unix-From or From_ header). If the message is a container (i.e. a - multipart or a message/rfc822), then the payload is a list of Message - objects, otherwise it is a string. - - Message objects implement part of the `mapping' interface, which assumes - there is exactly one occurrence of the header per message. Some headers - do in fact appear multiple times (e.g. Received) and for those headers, - you must use the explicit API to set or get all the headers. Not all of - the mapping methods are implemented. - """ - def __init__(self, policy=compat32): - self.policy = policy - self._headers = list() - self._unixfrom = None - self._payload = None - self._charset = None - # Defaults for multipart messages - self.preamble = self.epilogue = None - self.defects = [] - # Default content type - self._default_type = 'text/plain' - - @as_native_str(encoding='utf-8') - def __str__(self): - """Return the entire formatted message as a string. - This includes the headers, body, and envelope header. - """ - return self.as_string() - - def as_string(self, unixfrom=False, maxheaderlen=0): - """Return the entire formatted message as a (unicode) string. - Optional `unixfrom' when True, means include the Unix From_ envelope - header. - - This is a convenience method and may not generate the message exactly - as you intend. For more flexibility, use the flatten() method of a - Generator instance. - """ - from future.backports.email.generator import Generator - fp = StringIO() - g = Generator(fp, mangle_from_=False, maxheaderlen=maxheaderlen) - g.flatten(self, unixfrom=unixfrom) - return fp.getvalue() - - def is_multipart(self): - """Return True if the message consists of multiple parts.""" - return isinstance(self._payload, list) - - # - # Unix From_ line - # - def set_unixfrom(self, unixfrom): - self._unixfrom = unixfrom - - def get_unixfrom(self): - return self._unixfrom - - # - # Payload manipulation. - # - def attach(self, payload): - """Add the given payload to the current payload. - - The current payload will always be a list of objects after this method - is called. If you want to set the payload to a scalar object, use - set_payload() instead. - """ - if self._payload is None: - self._payload = [payload] - else: - self._payload.append(payload) - - def get_payload(self, i=None, decode=False): - """Return a reference to the payload. - - The payload will either be a list object or a string. If you mutate - the list object, you modify the message's payload in place. Optional - i returns that index into the payload. - - Optional decode is a flag indicating whether the payload should be - decoded or not, according to the Content-Transfer-Encoding header - (default is False). - - When True and the message is not a multipart, the payload will be - decoded if this header's value is `quoted-printable' or `base64'. If - some other encoding is used, or the header is missing, or if the - payload has bogus data (i.e. bogus base64 or uuencoded data), the - payload is returned as-is. - - If the message is a multipart and the decode flag is True, then None - is returned. - """ - # Here is the logic table for this code, based on the email5.0.0 code: - # i decode is_multipart result - # ------ ------ ------------ ------------------------------ - # None True True None - # i True True None - # None False True _payload (a list) - # i False True _payload element i (a Message) - # i False False error (not a list) - # i True False error (not a list) - # None False False _payload - # None True False _payload decoded (bytes) - # Note that Barry planned to factor out the 'decode' case, but that - # isn't so easy now that we handle the 8 bit data, which needs to be - # converted in both the decode and non-decode path. - if self.is_multipart(): - if decode: - return None - if i is None: - return self._payload - else: - return self._payload[i] - # For backward compatibility, Use isinstance and this error message - # instead of the more logical is_multipart test. - if i is not None and not isinstance(self._payload, list): - raise TypeError('Expected list, got %s' % type(self._payload)) - payload = self._payload - # cte might be a Header, so for now stringify it. - cte = str(self.get('content-transfer-encoding', '')).lower() - # payload may be bytes here. - if isinstance(payload, str): - payload = str(payload) # for Python-Future, so surrogateescape works - if utils._has_surrogates(payload): - bpayload = payload.encode('ascii', 'surrogateescape') - if not decode: - try: - payload = bpayload.decode(self.get_param('charset', 'ascii'), 'replace') - except LookupError: - payload = bpayload.decode('ascii', 'replace') - elif decode: - try: - bpayload = payload.encode('ascii') - except UnicodeError: - # This won't happen for RFC compliant messages (messages - # containing only ASCII codepoints in the unicode input). - # If it does happen, turn the string into bytes in a way - # guaranteed not to fail. - bpayload = payload.encode('raw-unicode-escape') - if not decode: - return payload - if cte == 'quoted-printable': - return utils._qdecode(bpayload) - elif cte == 'base64': - # XXX: this is a bit of a hack; decode_b should probably be factored - # out somewhere, but I haven't figured out where yet. - value, defects = decode_b(b''.join(bpayload.splitlines())) - for defect in defects: - self.policy.handle_defect(self, defect) - return value - elif cte in ('x-uuencode', 'uuencode', 'uue', 'x-uue'): - in_file = BytesIO(bpayload) - out_file = BytesIO() - try: - uu.decode(in_file, out_file, quiet=True) - return out_file.getvalue() - except uu.Error: - # Some decoding problem - return bpayload - if isinstance(payload, str): - return bpayload - return payload - - def set_payload(self, payload, charset=None): - """Set the payload to the given value. - - Optional charset sets the message's default character set. See - set_charset() for details. - """ - self._payload = payload - if charset is not None: - self.set_charset(charset) - - def set_charset(self, charset): - """Set the charset of the payload to a given character set. - - charset can be a Charset instance, a string naming a character set, or - None. If it is a string it will be converted to a Charset instance. - If charset is None, the charset parameter will be removed from the - Content-Type field. Anything else will generate a TypeError. - - The message will be assumed to be of type text/* encoded with - charset.input_charset. It will be converted to charset.output_charset - and encoded properly, if needed, when generating the plain text - representation of the message. MIME headers (MIME-Version, - Content-Type, Content-Transfer-Encoding) will be added as needed. - """ - if charset is None: - self.del_param('charset') - self._charset = None - return - if not isinstance(charset, Charset): - charset = Charset(charset) - self._charset = charset - if 'MIME-Version' not in self: - self.add_header('MIME-Version', '1.0') - if 'Content-Type' not in self: - self.add_header('Content-Type', 'text/plain', - charset=charset.get_output_charset()) - else: - self.set_param('charset', charset.get_output_charset()) - if charset != charset.get_output_charset(): - self._payload = charset.body_encode(self._payload) - if 'Content-Transfer-Encoding' not in self: - cte = charset.get_body_encoding() - try: - cte(self) - except TypeError: - self._payload = charset.body_encode(self._payload) - self.add_header('Content-Transfer-Encoding', cte) - - def get_charset(self): - """Return the Charset instance associated with the message's payload. - """ - return self._charset - - # - # MAPPING INTERFACE (partial) - # - def __len__(self): - """Return the total number of headers, including duplicates.""" - return len(self._headers) - - def __getitem__(self, name): - """Get a header value. - - Return None if the header is missing instead of raising an exception. - - Note that if the header appeared multiple times, exactly which - occurrence gets returned is undefined. Use get_all() to get all - the values matching a header field name. - """ - return self.get(name) - - def __setitem__(self, name, val): - """Set the value of a header. - - Note: this does not overwrite an existing header with the same field - name. Use __delitem__() first to delete any existing headers. - """ - max_count = self.policy.header_max_count(name) - if max_count: - lname = name.lower() - found = 0 - for k, v in self._headers: - if k.lower() == lname: - found += 1 - if found >= max_count: - raise ValueError("There may be at most {} {} headers " - "in a message".format(max_count, name)) - self._headers.append(self.policy.header_store_parse(name, val)) - - def __delitem__(self, name): - """Delete all occurrences of a header, if present. - - Does not raise an exception if the header is missing. - """ - name = name.lower() - newheaders = list() - for k, v in self._headers: - if k.lower() != name: - newheaders.append((k, v)) - self._headers = newheaders - - def __contains__(self, name): - return name.lower() in [k.lower() for k, v in self._headers] - - def __iter__(self): - for field, value in self._headers: - yield field - - def keys(self): - """Return a list of all the message's header field names. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [k for k, v in self._headers] - - def values(self): - """Return a list of all the message's header values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [self.policy.header_fetch_parse(k, v) - for k, v in self._headers] - - def items(self): - """Get all the message's header fields and values. - - These will be sorted in the order they appeared in the original - message, or were added to the message, and may contain duplicates. - Any fields deleted and re-inserted are always appended to the header - list. - """ - return [(k, self.policy.header_fetch_parse(k, v)) - for k, v in self._headers] - - def get(self, name, failobj=None): - """Get a header value. - - Like __getitem__() but return failobj instead of None when the field - is missing. - """ - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - return self.policy.header_fetch_parse(k, v) - return failobj - - # - # "Internal" methods (public API, but only intended for use by a parser - # or generator, not normal application code. - # - - def set_raw(self, name, value): - """Store name and value in the model without modification. - - This is an "internal" API, intended only for use by a parser. - """ - self._headers.append((name, value)) - - def raw_items(self): - """Return the (name, value) header pairs without modification. - - This is an "internal" API, intended only for use by a generator. - """ - return iter(self._headers.copy()) - - # - # Additional useful stuff - # - - def get_all(self, name, failobj=None): - """Return a list of all the values for the named field. - - These will be sorted in the order they appeared in the original - message, and may contain duplicates. Any fields deleted and - re-inserted are always appended to the header list. - - If no such fields exist, failobj is returned (defaults to None). - """ - values = [] - name = name.lower() - for k, v in self._headers: - if k.lower() == name: - values.append(self.policy.header_fetch_parse(k, v)) - if not values: - return failobj - return values - - def add_header(self, _name, _value, **_params): - """Extended header setting. - - name is the header field to add. keyword arguments can be used to set - additional parameters for the header field, with underscores converted - to dashes. Normally the parameter will be added as key="value" unless - value is None, in which case only the key will be added. If a - parameter value contains non-ASCII characters it can be specified as a - three-tuple of (charset, language, value), in which case it will be - encoded according to RFC2231 rules. Otherwise it will be encoded using - the utf-8 charset and a language of ''. - - Examples: - - msg.add_header('content-disposition', 'attachment', filename='bud.gif') - msg.add_header('content-disposition', 'attachment', - filename=('utf-8', '', 'Fußballer.ppt')) - msg.add_header('content-disposition', 'attachment', - filename='Fußballer.ppt')) - """ - parts = [] - for k, v in _params.items(): - if v is None: - parts.append(k.replace('_', '-')) - else: - parts.append(_formatparam(k.replace('_', '-'), v)) - if _value is not None: - parts.insert(0, _value) - self[_name] = SEMISPACE.join(parts) - - def replace_header(self, _name, _value): - """Replace a header. - - Replace the first matching header found in the message, retaining - header order and case. If no matching header was found, a KeyError is - raised. - """ - _name = _name.lower() - for i, (k, v) in zip(range(len(self._headers)), self._headers): - if k.lower() == _name: - self._headers[i] = self.policy.header_store_parse(k, _value) - break - else: - raise KeyError(_name) - - # - # Use these three methods instead of the three above. - # - - def get_content_type(self): - """Return the message's content type. - - The returned string is coerced to lower case of the form - `maintype/subtype'. If there was no Content-Type header in the - message, the default type as given by get_default_type() will be - returned. Since according to RFC 2045, messages always have a default - type this will always return a value. - - RFC 2045 defines a message's default type to be text/plain unless it - appears inside a multipart/digest container, in which case it would be - message/rfc822. - """ - missing = object() - value = self.get('content-type', missing) - if value is missing: - # This should have no parameters - return self.get_default_type() - ctype = _splitparam(value)[0].lower() - # RFC 2045, section 5.2 says if its invalid, use text/plain - if ctype.count('/') != 1: - return 'text/plain' - return ctype - - def get_content_maintype(self): - """Return the message's main content type. - - This is the `maintype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[0] - - def get_content_subtype(self): - """Returns the message's sub-content type. - - This is the `subtype' part of the string returned by - get_content_type(). - """ - ctype = self.get_content_type() - return ctype.split('/')[1] - - def get_default_type(self): - """Return the `default' content type. - - Most messages have a default content type of text/plain, except for - messages that are subparts of multipart/digest containers. Such - subparts have a default content type of message/rfc822. - """ - return self._default_type - - def set_default_type(self, ctype): - """Set the `default' content type. - - ctype should be either "text/plain" or "message/rfc822", although this - is not enforced. The default content type is not stored in the - Content-Type header. - """ - self._default_type = ctype - - def _get_params_preserve(self, failobj, header): - # Like get_params() but preserves the quoting of values. BAW: - # should this be part of the public interface? - missing = object() - value = self.get(header, missing) - if value is missing: - return failobj - params = [] - for p in _parseparam(value): - try: - name, val = p.split('=', 1) - name = name.strip() - val = val.strip() - except ValueError: - # Must have been a bare attribute - name = p.strip() - val = '' - params.append((name, val)) - params = utils.decode_params(params) - return params - - def get_params(self, failobj=None, header='content-type', unquote=True): - """Return the message's Content-Type parameters, as a list. - - The elements of the returned list are 2-tuples of key/value pairs, as - split on the `=' sign. The left hand side of the `=' is the key, - while the right hand side is the value. If there is no `=' sign in - the parameter the value is the empty string. The value is as - described in the get_param() method. - - Optional failobj is the object to return if there is no Content-Type - header. Optional header is the header to search instead of - Content-Type. If unquote is True, the value is unquoted. - """ - missing = object() - params = self._get_params_preserve(missing, header) - if params is missing: - return failobj - if unquote: - return [(k, _unquotevalue(v)) for k, v in params] - else: - return params - - def get_param(self, param, failobj=None, header='content-type', - unquote=True): - """Return the parameter value if found in the Content-Type header. - - Optional failobj is the object to return if there is no Content-Type - header, or the Content-Type header has no such parameter. Optional - header is the header to search instead of Content-Type. - - Parameter keys are always compared case insensitively. The return - value can either be a string, or a 3-tuple if the parameter was RFC - 2231 encoded. When it's a 3-tuple, the elements of the value are of - the form (CHARSET, LANGUAGE, VALUE). Note that both CHARSET and - LANGUAGE can be None, in which case you should consider VALUE to be - encoded in the us-ascii charset. You can usually ignore LANGUAGE. - The parameter value (either the returned string, or the VALUE item in - the 3-tuple) is always unquoted, unless unquote is set to False. - - If your application doesn't care whether the parameter was RFC 2231 - encoded, it can turn the return value into a string as follows: - - param = msg.get_param('foo') - param = email.utils.collapse_rfc2231_value(rawparam) - - """ - if header not in self: - return failobj - for k, v in self._get_params_preserve(failobj, header): - if k.lower() == param.lower(): - if unquote: - return _unquotevalue(v) - else: - return v - return failobj - - def set_param(self, param, value, header='Content-Type', requote=True, - charset=None, language=''): - """Set a parameter in the Content-Type header. - - If the parameter already exists in the header, its value will be - replaced with the new value. - - If header is Content-Type and has not yet been defined for this - message, it will be set to "text/plain" and the new parameter and - value will be appended as per RFC 2045. - - An alternate header can specified in the header argument, and all - parameters will be quoted as necessary unless requote is False. - - If charset is specified, the parameter will be encoded according to RFC - 2231. Optional language specifies the RFC 2231 language, defaulting - to the empty string. Both charset and language should be strings. - """ - if not isinstance(value, tuple) and charset: - value = (charset, language, value) - - if header not in self and header.lower() == 'content-type': - ctype = 'text/plain' - else: - ctype = self.get(header) - if not self.get_param(param, header=header): - if not ctype: - ctype = _formatparam(param, value, requote) - else: - ctype = SEMISPACE.join( - [ctype, _formatparam(param, value, requote)]) - else: - ctype = '' - for old_param, old_value in self.get_params(header=header, - unquote=requote): - append_param = '' - if old_param.lower() == param.lower(): - append_param = _formatparam(param, value, requote) - else: - append_param = _formatparam(old_param, old_value, requote) - if not ctype: - ctype = append_param - else: - ctype = SEMISPACE.join([ctype, append_param]) - if ctype != self.get(header): - del self[header] - self[header] = ctype - - def del_param(self, param, header='content-type', requote=True): - """Remove the given parameter completely from the Content-Type header. - - The header will be re-written in place without the parameter or its - value. All values will be quoted as necessary unless requote is - False. Optional header specifies an alternative to the Content-Type - header. - """ - if header not in self: - return - new_ctype = '' - for p, v in self.get_params(header=header, unquote=requote): - if p.lower() != param.lower(): - if not new_ctype: - new_ctype = _formatparam(p, v, requote) - else: - new_ctype = SEMISPACE.join([new_ctype, - _formatparam(p, v, requote)]) - if new_ctype != self.get(header): - del self[header] - self[header] = new_ctype - - def set_type(self, type, header='Content-Type', requote=True): - """Set the main type and subtype for the Content-Type header. - - type must be a string in the form "maintype/subtype", otherwise a - ValueError is raised. - - This method replaces the Content-Type header, keeping all the - parameters in place. If requote is False, this leaves the existing - header's quoting as is. Otherwise, the parameters will be quoted (the - default). - - An alternative header can be specified in the header argument. When - the Content-Type header is set, we'll always also add a MIME-Version - header. - """ - # BAW: should we be strict? - if not type.count('/') == 1: - raise ValueError - # Set the Content-Type, you get a MIME-Version - if header.lower() == 'content-type': - del self['mime-version'] - self['MIME-Version'] = '1.0' - if header not in self: - self[header] = type - return - params = self.get_params(header=header, unquote=requote) - del self[header] - self[header] = type - # Skip the first param; it's the old type. - for p, v in params[1:]: - self.set_param(p, v, header, requote) - - def get_filename(self, failobj=None): - """Return the filename associated with the payload if present. - - The filename is extracted from the Content-Disposition header's - `filename' parameter, and it is unquoted. If that header is missing - the `filename' parameter, this method falls back to looking for the - `name' parameter. - """ - missing = object() - filename = self.get_param('filename', missing, 'content-disposition') - if filename is missing: - filename = self.get_param('name', missing, 'content-type') - if filename is missing: - return failobj - return utils.collapse_rfc2231_value(filename).strip() - - def get_boundary(self, failobj=None): - """Return the boundary associated with the payload if present. - - The boundary is extracted from the Content-Type header's `boundary' - parameter, and it is unquoted. - """ - missing = object() - boundary = self.get_param('boundary', missing) - if boundary is missing: - return failobj - # RFC 2046 says that boundaries may begin but not end in w/s - return utils.collapse_rfc2231_value(boundary).rstrip() - - def set_boundary(self, boundary): - """Set the boundary parameter in Content-Type to 'boundary'. - - This is subtly different than deleting the Content-Type header and - adding a new one with a new boundary parameter via add_header(). The - main difference is that using the set_boundary() method preserves the - order of the Content-Type header in the original message. - - HeaderParseError is raised if the message has no Content-Type header. - """ - missing = object() - params = self._get_params_preserve(missing, 'content-type') - if params is missing: - # There was no Content-Type header, and we don't know what type - # to set it to, so raise an exception. - raise errors.HeaderParseError('No Content-Type header found') - newparams = [] - foundp = False - for pk, pv in params: - if pk.lower() == 'boundary': - newparams.append(('boundary', '"%s"' % boundary)) - foundp = True - else: - newparams.append((pk, pv)) - if not foundp: - # The original Content-Type header had no boundary attribute. - # Tack one on the end. BAW: should we raise an exception - # instead??? - newparams.append(('boundary', '"%s"' % boundary)) - # Replace the existing Content-Type header with the new value - newheaders = [] - for h, v in self._headers: - if h.lower() == 'content-type': - parts = [] - for k, v in newparams: - if v == '': - parts.append(k) - else: - parts.append('%s=%s' % (k, v)) - val = SEMISPACE.join(parts) - newheaders.append(self.policy.header_store_parse(h, val)) - - else: - newheaders.append((h, v)) - self._headers = newheaders - - def get_content_charset(self, failobj=None): - """Return the charset parameter of the Content-Type header. - - The returned string is always coerced to lower case. If there is no - Content-Type header, or if that header has no charset parameter, - failobj is returned. - """ - missing = object() - charset = self.get_param('charset', missing) - if charset is missing: - return failobj - if isinstance(charset, tuple): - # RFC 2231 encoded, so decode it, and it better end up as ascii. - pcharset = charset[0] or 'us-ascii' - try: - # LookupError will be raised if the charset isn't known to - # Python. UnicodeError will be raised if the encoded text - # contains a character not in the charset. - as_bytes = charset[2].encode('raw-unicode-escape') - charset = str(as_bytes, pcharset) - except (LookupError, UnicodeError): - charset = charset[2] - # charset characters must be in us-ascii range - try: - charset.encode('us-ascii') - except UnicodeError: - return failobj - # RFC 2046, $4.1.2 says charsets are not case sensitive - return charset.lower() - - def get_charsets(self, failobj=None): - """Return a list containing the charset(s) used in this message. - - The returned list of items describes the Content-Type headers' - charset parameter for this message and all the subparts in its - payload. - - Each item will either be a string (the value of the charset parameter - in the Content-Type header of that part) or the value of the - 'failobj' parameter (defaults to None), if the part does not have a - main MIME type of "text", or the charset is not defined. - - The list will contain one string for each part of the message, plus - one for the container message (i.e. self), so that a non-multipart - message will still return a list of length 1. - """ - return [part.get_content_charset(failobj) for part in self.walk()] - - # I.e. def walk(self): ... - from future.backports.email.iterators import walk diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/application.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/application.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/application.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/application.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Keith Dart -# Contact: email-sig@python.org - -"""Class representing application/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - -__all__ = ["MIMEApplication"] - - -class MIMEApplication(MIMENonMultipart): - """Class for generating application/* MIME documents.""" - - def __init__(self, _data, _subtype='octet-stream', - _encoder=encoders.encode_base64, **_params): - """Create an application/* type MIME document. - - _data is a string containing the raw application data. - - _subtype is the MIME content type subtype, defaulting to - 'octet-stream'. - - _encoder is a function which will perform the actual encoding for - transport of the application data, defaulting to base64 encoding. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - raise TypeError('Invalid application MIME subtype') - MIMENonMultipart.__init__(self, 'application', _subtype, **_params) - self.set_payload(_data) - _encoder(self) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/audio.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/audio.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/audio.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/audio.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Anthony Baxter -# Contact: email-sig@python.org - -"""Class representing audio/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEAudio'] - -import sndhdr - -from io import BytesIO -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -_sndhdr_MIMEmap = {'au' : 'basic', - 'wav' :'x-wav', - 'aiff':'x-aiff', - 'aifc':'x-aiff', - } - -# There are others in sndhdr that don't have MIME types. :( -# Additional ones to be added to sndhdr? midi, mp3, realaudio, wma?? -def _whatsnd(data): - """Try to identify a sound file type. - - sndhdr.what() has a pretty cruddy interface, unfortunately. This is why - we re-do it here. It would be easier to reverse engineer the Unix 'file' - command and use the standard 'magic' file, as shipped with a modern Unix. - """ - hdr = data[:512] - fakefile = BytesIO(hdr) - for testfn in sndhdr.tests: - res = testfn(hdr, fakefile) - if res is not None: - return _sndhdr_MIMEmap.get(res[0]) - return None - - -class MIMEAudio(MIMENonMultipart): - """Class for generating audio/* MIME documents.""" - - def __init__(self, _audiodata, _subtype=None, - _encoder=encoders.encode_base64, **_params): - """Create an audio/* type MIME document. - - _audiodata is a string containing the raw audio data. If this data - can be decoded by the standard Python `sndhdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific audio subtype via the - _subtype parameter. If _subtype is not given, and no subtype can be - guessed, a TypeError is raised. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = _whatsnd(_audiodata) - if _subtype is None: - raise TypeError('Could not find audio MIME subtype') - MIMENonMultipart.__init__(self, 'audio', _subtype, **_params) - self.set_payload(_audiodata) - _encoder(self) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/base.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/base.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME specializations.""" -from __future__ import absolute_import, division, unicode_literals -from future.backports.email import message - -__all__ = ['MIMEBase'] - - -class MIMEBase(message.Message): - """Base class for MIME specializations.""" - - def __init__(self, _maintype, _subtype, **_params): - """This constructor adds a Content-Type: and a MIME-Version: header. - - The Content-Type: header is taken from the _maintype and _subtype - arguments. Additional parameters for this header are taken from the - keyword arguments. - """ - message.Message.__init__(self) - ctype = '%s/%s' % (_maintype, _subtype) - self.add_header('Content-Type', ctype, **_params) - self['MIME-Version'] = '1.0' diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/image.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/image.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/image.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/image.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing image/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEImage'] - -import imghdr - -from future.backports.email import encoders -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEImage(MIMENonMultipart): - """Class for generating image/* type MIME documents.""" - - def __init__(self, _imagedata, _subtype=None, - _encoder=encoders.encode_base64, **_params): - """Create an image/* type MIME document. - - _imagedata is a string containing the raw image data. If this data - can be decoded by the standard Python `imghdr' module, then the - subtype will be automatically included in the Content-Type header. - Otherwise, you can specify the specific image subtype via the _subtype - parameter. - - _encoder is a function which will perform the actual encoding for - transport of the image data. It takes one argument, which is this - Image instance. It should use get_payload() and set_payload() to - change the payload to the encoded form. It should also add any - Content-Transfer-Encoding or other headers to the message as - necessary. The default encoding is Base64. - - Any additional keyword arguments are passed to the base class - constructor, which turns them into parameters on the Content-Type - header. - """ - if _subtype is None: - _subtype = imghdr.what(None, _imagedata) - if _subtype is None: - raise TypeError('Could not guess image MIME subtype') - MIMENonMultipart.__init__(self, 'image', _subtype, **_params) - self.set_payload(_imagedata) - _encoder(self) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/message.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/message.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/message.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/message.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing message/* MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEMessage'] - -from future.backports.email import message -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEMessage(MIMENonMultipart): - """Class representing message/* MIME documents.""" - - def __init__(self, _msg, _subtype='rfc822'): - """Create a message/* type MIME document. - - _msg is a message object and must be an instance of Message, or a - derived class of Message, otherwise a TypeError is raised. - - Optional _subtype defines the subtype of the contained message. The - default is "rfc822" (this is defined by the MIME standard, even though - the term "rfc822" is technically outdated by RFC 2822). - """ - MIMENonMultipart.__init__(self, 'message', _subtype) - if not isinstance(_msg, message.Message): - raise TypeError('Argument is not an instance of Message') - # It's convenient to use this base class method. We need to do it - # this way or we'll get an exception - message.Message.attach(self, _msg) - # And be sure our default type is set correctly - self.set_default_type('message/rfc822') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/multipart.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/multipart.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/multipart.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/multipart.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -# Copyright (C) 2002-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME multipart/* type messages.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEMultipart'] - -from future.backports.email.mime.base import MIMEBase - - -class MIMEMultipart(MIMEBase): - """Base class for MIME multipart/* type messages.""" - - def __init__(self, _subtype='mixed', boundary=None, _subparts=None, - **_params): - """Creates a multipart/* type message. - - By default, creates a multipart/mixed message, with proper - Content-Type and MIME-Version headers. - - _subtype is the subtype of the multipart content type, defaulting to - `mixed'. - - boundary is the multipart boundary string. By default it is - calculated as needed. - - _subparts is a sequence of initial subparts for the payload. It - must be an iterable object, such as a list. You can always - attach new subparts to the message by using the attach() method. - - Additional parameters for the Content-Type header are taken from the - keyword arguments (or passed into the _params argument). - """ - MIMEBase.__init__(self, 'multipart', _subtype, **_params) - - # Initialise _payload to an empty list as the Message superclass's - # implementation of is_multipart assumes that _payload is a list for - # multipart messages. - self._payload = [] - - if _subparts: - for p in _subparts: - self.attach(p) - if boundary: - self.set_boundary(boundary) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/nonmultipart.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -# Copyright (C) 2002-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Base class for MIME type messages that are not multipart.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMENonMultipart'] - -from future.backports.email import errors -from future.backports.email.mime.base import MIMEBase - - -class MIMENonMultipart(MIMEBase): - """Base class for MIME multipart/* type messages.""" - - def attach(self, payload): - # The public API prohibits attaching multiple subparts to MIMEBase - # derived subtypes since none of them are, by definition, of content - # type multipart/* - raise errors.MultipartConversionError( - 'Cannot attach additional subparts to non-multipart/*') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/text.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/text.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/mime/text.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/mime/text.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Class representing text/* type MIME documents.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['MIMEText'] - -from future.backports.email.encoders import encode_7or8bit -from future.backports.email.mime.nonmultipart import MIMENonMultipart - - -class MIMEText(MIMENonMultipart): - """Class for generating text/* type MIME documents.""" - - def __init__(self, _text, _subtype='plain', _charset=None): - """Create a text/* type MIME document. - - _text is the string for this message object. - - _subtype is the MIME sub content type, defaulting to "plain". - - _charset is the character set parameter added to the Content-Type - header. This defaults to "us-ascii". Note that as a side-effect, the - Content-Transfer-Encoding header will also be set. - """ - - # If no _charset was specified, check to see if there are non-ascii - # characters present. If not, use 'us-ascii', otherwise use utf-8. - # XXX: This can be removed once #7304 is fixed. - if _charset is None: - try: - _text.encode('us-ascii') - _charset = 'us-ascii' - except UnicodeEncodeError: - _charset = 'utf-8' - - MIMENonMultipart.__init__(self, 'text', _subtype, - **{'charset': _charset}) - - self.set_payload(_text, _charset) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_parseaddr.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_parseaddr.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_parseaddr.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_parseaddr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,546 +0,0 @@ -# Copyright (C) 2002-2007 Python Software Foundation -# Contact: email-sig@python.org - -"""Email address parsing code. - -Lifted directly from rfc822.py. This should eventually be rewritten. -""" - -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import int - -__all__ = [ - 'mktime_tz', - 'parsedate', - 'parsedate_tz', - 'quote', - ] - -import time, calendar - -SPACE = ' ' -EMPTYSTRING = '' -COMMASPACE = ', ' - -# Parse a date field -_monthnames = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', - 'aug', 'sep', 'oct', 'nov', 'dec', - 'january', 'february', 'march', 'april', 'may', 'june', 'july', - 'august', 'september', 'october', 'november', 'december'] - -_daynames = ['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'] - -# The timezone table does not include the military time zones defined -# in RFC822, other than Z. According to RFC1123, the description in -# RFC822 gets the signs wrong, so we can't rely on any such time -# zones. RFC1123 recommends that numeric timezone indicators be used -# instead of timezone names. - -_timezones = {'UT':0, 'UTC':0, 'GMT':0, 'Z':0, - 'AST': -400, 'ADT': -300, # Atlantic (used in Canada) - 'EST': -500, 'EDT': -400, # Eastern - 'CST': -600, 'CDT': -500, # Central - 'MST': -700, 'MDT': -600, # Mountain - 'PST': -800, 'PDT': -700 # Pacific - } - - -def parsedate_tz(data): - """Convert a date string to a time tuple. - - Accounts for military timezones. - """ - res = _parsedate_tz(data) - if not res: - return - if res[9] is None: - res[9] = 0 - return tuple(res) - -def _parsedate_tz(data): - """Convert date to extended time tuple. - - The last (additional) element is the time zone offset in seconds, except if - the timezone was specified as -0000. In that case the last element is - None. This indicates a UTC timestamp that explicitly declaims knowledge of - the source timezone, as opposed to a +0000 timestamp that indicates the - source timezone really was UTC. - - """ - if not data: - return - data = data.split() - # The FWS after the comma after the day-of-week is optional, so search and - # adjust for this. - if data[0].endswith(',') or data[0].lower() in _daynames: - # There's a dayname here. Skip it - del data[0] - else: - i = data[0].rfind(',') - if i >= 0: - data[0] = data[0][i+1:] - if len(data) == 3: # RFC 850 date, deprecated - stuff = data[0].split('-') - if len(stuff) == 3: - data = stuff + data[1:] - if len(data) == 4: - s = data[3] - i = s.find('+') - if i == -1: - i = s.find('-') - if i > 0: - data[3:] = [s[:i], s[i:]] - else: - data.append('') # Dummy tz - if len(data) < 5: - return None - data = data[:5] - [dd, mm, yy, tm, tz] = data - mm = mm.lower() - if mm not in _monthnames: - dd, mm = mm, dd.lower() - if mm not in _monthnames: - return None - mm = _monthnames.index(mm) + 1 - if mm > 12: - mm -= 12 - if dd[-1] == ',': - dd = dd[:-1] - i = yy.find(':') - if i > 0: - yy, tm = tm, yy - if yy[-1] == ',': - yy = yy[:-1] - if not yy[0].isdigit(): - yy, tz = tz, yy - if tm[-1] == ',': - tm = tm[:-1] - tm = tm.split(':') - if len(tm) == 2: - [thh, tmm] = tm - tss = '0' - elif len(tm) == 3: - [thh, tmm, tss] = tm - elif len(tm) == 1 and '.' in tm[0]: - # Some non-compliant MUAs use '.' to separate time elements. - tm = tm[0].split('.') - if len(tm) == 2: - [thh, tmm] = tm - tss = 0 - elif len(tm) == 3: - [thh, tmm, tss] = tm - else: - return None - try: - yy = int(yy) - dd = int(dd) - thh = int(thh) - tmm = int(tmm) - tss = int(tss) - except ValueError: - return None - # Check for a yy specified in two-digit format, then convert it to the - # appropriate four-digit format, according to the POSIX standard. RFC 822 - # calls for a two-digit yy, but RFC 2822 (which obsoletes RFC 822) - # mandates a 4-digit yy. For more information, see the documentation for - # the time module. - if yy < 100: - # The year is between 1969 and 1999 (inclusive). - if yy > 68: - yy += 1900 - # The year is between 2000 and 2068 (inclusive). - else: - yy += 2000 - tzoffset = None - tz = tz.upper() - if tz in _timezones: - tzoffset = _timezones[tz] - else: - try: - tzoffset = int(tz) - except ValueError: - pass - if tzoffset==0 and tz.startswith('-'): - tzoffset = None - # Convert a timezone offset into seconds ; -0500 -> -18000 - if tzoffset: - if tzoffset < 0: - tzsign = -1 - tzoffset = -tzoffset - else: - tzsign = 1 - tzoffset = tzsign * ( (tzoffset//100)*3600 + (tzoffset % 100)*60) - # Daylight Saving Time flag is set to -1, since DST is unknown. - return [yy, mm, dd, thh, tmm, tss, 0, 1, -1, tzoffset] - - -def parsedate(data): - """Convert a time string to a time tuple.""" - t = parsedate_tz(data) - if isinstance(t, tuple): - return t[:9] - else: - return t - - -def mktime_tz(data): - """Turn a 10-tuple as returned by parsedate_tz() into a POSIX timestamp.""" - if data[9] is None: - # No zone info, so localtime is better assumption than GMT - return time.mktime(data[:8] + (-1,)) - else: - t = calendar.timegm(data) - return t - data[9] - - -def quote(str): - """Prepare string to be used in a quoted string. - - Turns backslash and double quote characters into quoted pairs. These - are the only characters that need to be quoted inside a quoted string. - Does not add the surrounding double quotes. - """ - return str.replace('\\', '\\\\').replace('"', '\\"') - - -class AddrlistClass(object): - """Address parser class by Ben Escoto. - - To understand what this class does, it helps to have a copy of RFC 2822 in - front of you. - - Note: this class interface is deprecated and may be removed in the future. - Use email.utils.AddressList instead. - """ - - def __init__(self, field): - """Initialize a new instance. - - `field' is an unparsed address header field, containing - one or more addresses. - """ - self.specials = '()<>@,:;.\"[]' - self.pos = 0 - self.LWS = ' \t' - self.CR = '\r\n' - self.FWS = self.LWS + self.CR - self.atomends = self.specials + self.LWS + self.CR - # Note that RFC 2822 now specifies `.' as obs-phrase, meaning that it - # is obsolete syntax. RFC 2822 requires that we recognize obsolete - # syntax, so allow dots in phrases. - self.phraseends = self.atomends.replace('.', '') - self.field = field - self.commentlist = [] - - def gotonext(self): - """Skip white space and extract comments.""" - wslist = [] - while self.pos < len(self.field): - if self.field[self.pos] in self.LWS + '\n\r': - if self.field[self.pos] not in '\n\r': - wslist.append(self.field[self.pos]) - self.pos += 1 - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - else: - break - return EMPTYSTRING.join(wslist) - - def getaddrlist(self): - """Parse all addresses. - - Returns a list containing all of the addresses. - """ - result = [] - while self.pos < len(self.field): - ad = self.getaddress() - if ad: - result += ad - else: - result.append(('', '')) - return result - - def getaddress(self): - """Parse the next address.""" - self.commentlist = [] - self.gotonext() - - oldpos = self.pos - oldcl = self.commentlist - plist = self.getphraselist() - - self.gotonext() - returnlist = [] - - if self.pos >= len(self.field): - # Bad email address technically, no domain. - if plist: - returnlist = [(SPACE.join(self.commentlist), plist[0])] - - elif self.field[self.pos] in '.@': - # email address is just an addrspec - # this isn't very efficient since we start over - self.pos = oldpos - self.commentlist = oldcl - addrspec = self.getaddrspec() - returnlist = [(SPACE.join(self.commentlist), addrspec)] - - elif self.field[self.pos] == ':': - # address is a group - returnlist = [] - - fieldlen = len(self.field) - self.pos += 1 - while self.pos < len(self.field): - self.gotonext() - if self.pos < fieldlen and self.field[self.pos] == ';': - self.pos += 1 - break - returnlist = returnlist + self.getaddress() - - elif self.field[self.pos] == '<': - # Address is a phrase then a route addr - routeaddr = self.getrouteaddr() - - if self.commentlist: - returnlist = [(SPACE.join(plist) + ' (' + - ' '.join(self.commentlist) + ')', routeaddr)] - else: - returnlist = [(SPACE.join(plist), routeaddr)] - - else: - if plist: - returnlist = [(SPACE.join(self.commentlist), plist[0])] - elif self.field[self.pos] in self.specials: - self.pos += 1 - - self.gotonext() - if self.pos < len(self.field) and self.field[self.pos] == ',': - self.pos += 1 - return returnlist - - def getrouteaddr(self): - """Parse a route address (Return-path value). - - This method just skips all the route stuff and returns the addrspec. - """ - if self.field[self.pos] != '<': - return - - expectroute = False - self.pos += 1 - self.gotonext() - adlist = '' - while self.pos < len(self.field): - if expectroute: - self.getdomain() - expectroute = False - elif self.field[self.pos] == '>': - self.pos += 1 - break - elif self.field[self.pos] == '@': - self.pos += 1 - expectroute = True - elif self.field[self.pos] == ':': - self.pos += 1 - else: - adlist = self.getaddrspec() - self.pos += 1 - break - self.gotonext() - - return adlist - - def getaddrspec(self): - """Parse an RFC 2822 addr-spec.""" - aslist = [] - - self.gotonext() - while self.pos < len(self.field): - preserve_ws = True - if self.field[self.pos] == '.': - if aslist and not aslist[-1].strip(): - aslist.pop() - aslist.append('.') - self.pos += 1 - preserve_ws = False - elif self.field[self.pos] == '"': - aslist.append('"%s"' % quote(self.getquote())) - elif self.field[self.pos] in self.atomends: - if aslist and not aslist[-1].strip(): - aslist.pop() - break - else: - aslist.append(self.getatom()) - ws = self.gotonext() - if preserve_ws and ws: - aslist.append(ws) - - if self.pos >= len(self.field) or self.field[self.pos] != '@': - return EMPTYSTRING.join(aslist) - - aslist.append('@') - self.pos += 1 - self.gotonext() - return EMPTYSTRING.join(aslist) + self.getdomain() - - def getdomain(self): - """Get the complete domain name from an address.""" - sdlist = [] - while self.pos < len(self.field): - if self.field[self.pos] in self.LWS: - self.pos += 1 - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - elif self.field[self.pos] == '[': - sdlist.append(self.getdomainliteral()) - elif self.field[self.pos] == '.': - self.pos += 1 - sdlist.append('.') - elif self.field[self.pos] in self.atomends: - break - else: - sdlist.append(self.getatom()) - return EMPTYSTRING.join(sdlist) - - def getdelimited(self, beginchar, endchars, allowcomments=True): - """Parse a header fragment delimited by special characters. - - `beginchar' is the start character for the fragment. - If self is not looking at an instance of `beginchar' then - getdelimited returns the empty string. - - `endchars' is a sequence of allowable end-delimiting characters. - Parsing stops when one of these is encountered. - - If `allowcomments' is non-zero, embedded RFC 2822 comments are allowed - within the parsed fragment. - """ - if self.field[self.pos] != beginchar: - return '' - - slist = [''] - quote = False - self.pos += 1 - while self.pos < len(self.field): - if quote: - slist.append(self.field[self.pos]) - quote = False - elif self.field[self.pos] in endchars: - self.pos += 1 - break - elif allowcomments and self.field[self.pos] == '(': - slist.append(self.getcomment()) - continue # have already advanced pos from getcomment - elif self.field[self.pos] == '\\': - quote = True - else: - slist.append(self.field[self.pos]) - self.pos += 1 - - return EMPTYSTRING.join(slist) - - def getquote(self): - """Get a quote-delimited fragment from self's field.""" - return self.getdelimited('"', '"\r', False) - - def getcomment(self): - """Get a parenthesis-delimited fragment from self's field.""" - return self.getdelimited('(', ')\r', True) - - def getdomainliteral(self): - """Parse an RFC 2822 domain-literal.""" - return '[%s]' % self.getdelimited('[', ']\r', False) - - def getatom(self, atomends=None): - """Parse an RFC 2822 atom. - - Optional atomends specifies a different set of end token delimiters - (the default is to use self.atomends). This is used e.g. in - getphraselist() since phrase endings must not include the `.' (which - is legal in phrases).""" - atomlist = [''] - if atomends is None: - atomends = self.atomends - - while self.pos < len(self.field): - if self.field[self.pos] in atomends: - break - else: - atomlist.append(self.field[self.pos]) - self.pos += 1 - - return EMPTYSTRING.join(atomlist) - - def getphraselist(self): - """Parse a sequence of RFC 2822 phrases. - - A phrase is a sequence of words, which are in turn either RFC 2822 - atoms or quoted-strings. Phrases are canonicalized by squeezing all - runs of continuous whitespace into one space. - """ - plist = [] - - while self.pos < len(self.field): - if self.field[self.pos] in self.FWS: - self.pos += 1 - elif self.field[self.pos] == '"': - plist.append(self.getquote()) - elif self.field[self.pos] == '(': - self.commentlist.append(self.getcomment()) - elif self.field[self.pos] in self.phraseends: - break - else: - plist.append(self.getatom(self.phraseends)) - - return plist - -class AddressList(AddrlistClass): - """An AddressList encapsulates a list of parsed RFC 2822 addresses.""" - def __init__(self, field): - AddrlistClass.__init__(self, field) - if field: - self.addresslist = self.getaddrlist() - else: - self.addresslist = [] - - def __len__(self): - return len(self.addresslist) - - def __add__(self, other): - # Set union - newaddr = AddressList(None) - newaddr.addresslist = self.addresslist[:] - for x in other.addresslist: - if not x in self.addresslist: - newaddr.addresslist.append(x) - return newaddr - - def __iadd__(self, other): - # Set union, in-place - for x in other.addresslist: - if not x in self.addresslist: - self.addresslist.append(x) - return self - - def __sub__(self, other): - # Set difference - newaddr = AddressList(None) - for x in self.addresslist: - if not x in other.addresslist: - newaddr.addresslist.append(x) - return newaddr - - def __isub__(self, other): - # Set difference, in-place - for x in other.addresslist: - if x in self.addresslist: - self.addresslist.remove(x) - return self - - def __getitem__(self, index): - # Make indexing, slices, and 'in' work - return self.addresslist[index] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/parser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/parser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -# Copyright (C) 2001-2007 Python Software Foundation -# Author: Barry Warsaw, Thomas Wouters, Anthony Baxter -# Contact: email-sig@python.org - -"""A parser of RFC 2822 and MIME email messages.""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import - -__all__ = ['Parser', 'HeaderParser', 'BytesParser', 'BytesHeaderParser'] - -import warnings -from io import StringIO, TextIOWrapper - -from future.backports.email.feedparser import FeedParser, BytesFeedParser -from future.backports.email.message import Message -from future.backports.email._policybase import compat32 - - -class Parser(object): - def __init__(self, _class=Message, **_3to2kwargs): - """Parser of RFC 2822 and MIME email messages. - - Creates an in-memory object tree representing the email message, which - can then be manipulated and turned over to a Generator to return the - textual representation of the message. - - The string must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The - header block is terminated either by the end of the string or by a - blank line. - - _class is the class to instantiate for new message objects when they - must be created. This class must have a constructor that can take - zero arguments. Default is Message.Message. - - The policy keyword specifies a policy object that controls a number of - aspects of the parser's operation. The default policy maintains - backward compatibility. - - """ - if 'policy' in _3to2kwargs: policy = _3to2kwargs['policy']; del _3to2kwargs['policy'] - else: policy = compat32 - self._class = _class - self.policy = policy - - def parse(self, fp, headersonly=False): - """Create a message structure from the data in a file. - - Reads all the data from the file and returns the root of the message - structure. Optional headersonly is a flag specifying whether to stop - parsing after reading the headers or not. The default is False, - meaning it parses the entire contents of the file. - """ - feedparser = FeedParser(self._class, policy=self.policy) - if headersonly: - feedparser._set_headersonly() - while True: - data = fp.read(8192) - if not data: - break - feedparser.feed(data) - return feedparser.close() - - def parsestr(self, text, headersonly=False): - """Create a message structure from a string. - - Returns the root of the message structure. Optional headersonly is a - flag specifying whether to stop parsing after reading the headers or - not. The default is False, meaning it parses the entire contents of - the file. - """ - return self.parse(StringIO(text), headersonly=headersonly) - - - -class HeaderParser(Parser): - def parse(self, fp, headersonly=True): - return Parser.parse(self, fp, True) - - def parsestr(self, text, headersonly=True): - return Parser.parsestr(self, text, True) - - -class BytesParser(object): - - def __init__(self, *args, **kw): - """Parser of binary RFC 2822 and MIME email messages. - - Creates an in-memory object tree representing the email message, which - can then be manipulated and turned over to a Generator to return the - textual representation of the message. - - The input must be formatted as a block of RFC 2822 headers and header - continuation lines, optionally preceeded by a `Unix-from' header. The - header block is terminated either by the end of the input or by a - blank line. - - _class is the class to instantiate for new message objects when they - must be created. This class must have a constructor that can take - zero arguments. Default is Message.Message. - """ - self.parser = Parser(*args, **kw) - - def parse(self, fp, headersonly=False): - """Create a message structure from the data in a binary file. - - Reads all the data from the file and returns the root of the message - structure. Optional headersonly is a flag specifying whether to stop - parsing after reading the headers or not. The default is False, - meaning it parses the entire contents of the file. - """ - fp = TextIOWrapper(fp, encoding='ascii', errors='surrogateescape') - with fp: - return self.parser.parse(fp, headersonly) - - - def parsebytes(self, text, headersonly=False): - """Create a message structure from a byte string. - - Returns the root of the message structure. Optional headersonly is a - flag specifying whether to stop parsing after reading the headers or - not. The default is False, meaning it parses the entire contents of - the file. - """ - text = text.decode('ASCII', errors='surrogateescape') - return self.parser.parsestr(text, headersonly) - - -class BytesHeaderParser(BytesParser): - def parse(self, fp, headersonly=True): - return BytesParser.parse(self, fp, headersonly=True) - - def parsebytes(self, text, headersonly=True): - return BytesParser.parsebytes(self, text, headersonly=True) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_policybase.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_policybase.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/_policybase.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/_policybase.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,365 +0,0 @@ -"""Policy framework for the email package. - -Allows fine grained feature control of how the package parses and emits data. -""" -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import super -from future.builtins import str -from future.utils import with_metaclass - -import abc -from future.backports.email import header -from future.backports.email import charset as _charset -from future.backports.email.utils import _has_surrogates - -__all__ = [ - 'Policy', - 'Compat32', - 'compat32', - ] - - -class _PolicyBase(object): - - """Policy Object basic framework. - - This class is useless unless subclassed. A subclass should define - class attributes with defaults for any values that are to be - managed by the Policy object. The constructor will then allow - non-default values to be set for these attributes at instance - creation time. The instance will be callable, taking these same - attributes keyword arguments, and returning a new instance - identical to the called instance except for those values changed - by the keyword arguments. Instances may be added, yielding new - instances with any non-default values from the right hand - operand overriding those in the left hand operand. That is, - - A + B == A() - - The repr of an instance can be used to reconstruct the object - if and only if the repr of the values can be used to reconstruct - those values. - - """ - - def __init__(self, **kw): - """Create new Policy, possibly overriding some defaults. - - See class docstring for a list of overridable attributes. - - """ - for name, value in kw.items(): - if hasattr(self, name): - super(_PolicyBase,self).__setattr__(name, value) - else: - raise TypeError( - "{!r} is an invalid keyword argument for {}".format( - name, self.__class__.__name__)) - - def __repr__(self): - args = [ "{}={!r}".format(name, value) - for name, value in self.__dict__.items() ] - return "{}({})".format(self.__class__.__name__, ', '.join(args)) - - def clone(self, **kw): - """Return a new instance with specified attributes changed. - - The new instance has the same attribute values as the current object, - except for the changes passed in as keyword arguments. - - """ - newpolicy = self.__class__.__new__(self.__class__) - for attr, value in self.__dict__.items(): - object.__setattr__(newpolicy, attr, value) - for attr, value in kw.items(): - if not hasattr(self, attr): - raise TypeError( - "{!r} is an invalid keyword argument for {}".format( - attr, self.__class__.__name__)) - object.__setattr__(newpolicy, attr, value) - return newpolicy - - def __setattr__(self, name, value): - if hasattr(self, name): - msg = "{!r} object attribute {!r} is read-only" - else: - msg = "{!r} object has no attribute {!r}" - raise AttributeError(msg.format(self.__class__.__name__, name)) - - def __add__(self, other): - """Non-default values from right operand override those from left. - - The object returned is a new instance of the subclass. - - """ - return self.clone(**other.__dict__) - - -def _append_doc(doc, added_doc): - doc = doc.rsplit('\n', 1)[0] - added_doc = added_doc.split('\n', 1)[1] - return doc + '\n' + added_doc - -def _extend_docstrings(cls): - if cls.__doc__ and cls.__doc__.startswith('+'): - cls.__doc__ = _append_doc(cls.__bases__[0].__doc__, cls.__doc__) - for name, attr in cls.__dict__.items(): - if attr.__doc__ and attr.__doc__.startswith('+'): - for c in (c for base in cls.__bases__ for c in base.mro()): - doc = getattr(getattr(c, name), '__doc__') - if doc: - attr.__doc__ = _append_doc(doc, attr.__doc__) - break - return cls - - -class Policy(with_metaclass(abc.ABCMeta, _PolicyBase)): - - r"""Controls for how messages are interpreted and formatted. - - Most of the classes and many of the methods in the email package accept - Policy objects as parameters. A Policy object contains a set of values and - functions that control how input is interpreted and how output is rendered. - For example, the parameter 'raise_on_defect' controls whether or not an RFC - violation results in an error being raised or not, while 'max_line_length' - controls the maximum length of output lines when a Message is serialized. - - Any valid attribute may be overridden when a Policy is created by passing - it as a keyword argument to the constructor. Policy objects are immutable, - but a new Policy object can be created with only certain values changed by - calling the Policy instance with keyword arguments. Policy objects can - also be added, producing a new Policy object in which the non-default - attributes set in the right hand operand overwrite those specified in the - left operand. - - Settable attributes: - - raise_on_defect -- If true, then defects should be raised as errors. - Default: False. - - linesep -- string containing the value to use as separation - between output lines. Default '\n'. - - cte_type -- Type of allowed content transfer encodings - - 7bit -- ASCII only - 8bit -- Content-Transfer-Encoding: 8bit is allowed - - Default: 8bit. Also controls the disposition of - (RFC invalid) binary data in headers; see the - documentation of the binary_fold method. - - max_line_length -- maximum length of lines, excluding 'linesep', - during serialization. None or 0 means no line - wrapping is done. Default is 78. - - """ - - raise_on_defect = False - linesep = '\n' - cte_type = '8bit' - max_line_length = 78 - - def handle_defect(self, obj, defect): - """Based on policy, either raise defect or call register_defect. - - handle_defect(obj, defect) - - defect should be a Defect subclass, but in any case must be an - Exception subclass. obj is the object on which the defect should be - registered if it is not raised. If the raise_on_defect is True, the - defect is raised as an error, otherwise the object and the defect are - passed to register_defect. - - This method is intended to be called by parsers that discover defects. - The email package parsers always call it with Defect instances. - - """ - if self.raise_on_defect: - raise defect - self.register_defect(obj, defect) - - def register_defect(self, obj, defect): - """Record 'defect' on 'obj'. - - Called by handle_defect if raise_on_defect is False. This method is - part of the Policy API so that Policy subclasses can implement custom - defect handling. The default implementation calls the append method of - the defects attribute of obj. The objects used by the email package by - default that get passed to this method will always have a defects - attribute with an append method. - - """ - obj.defects.append(defect) - - def header_max_count(self, name): - """Return the maximum allowed number of headers named 'name'. - - Called when a header is added to a Message object. If the returned - value is not 0 or None, and there are already a number of headers with - the name 'name' equal to the value returned, a ValueError is raised. - - Because the default behavior of Message's __setitem__ is to append the - value to the list of headers, it is easy to create duplicate headers - without realizing it. This method allows certain headers to be limited - in the number of instances of that header that may be added to a - Message programmatically. (The limit is not observed by the parser, - which will faithfully produce as many headers as exist in the message - being parsed.) - - The default implementation returns None for all header names. - """ - return None - - @abc.abstractmethod - def header_source_parse(self, sourcelines): - """Given a list of linesep terminated strings constituting the lines of - a single header, return the (name, value) tuple that should be stored - in the model. The input lines should retain their terminating linesep - characters. The lines passed in by the email package may contain - surrogateescaped binary data. - """ - raise NotImplementedError - - @abc.abstractmethod - def header_store_parse(self, name, value): - """Given the header name and the value provided by the application - program, return the (name, value) that should be stored in the model. - """ - raise NotImplementedError - - @abc.abstractmethod - def header_fetch_parse(self, name, value): - """Given the header name and the value from the model, return the value - to be returned to the application program that is requesting that - header. The value passed in by the email package may contain - surrogateescaped binary data if the lines were parsed by a BytesParser. - The returned value should not contain any surrogateescaped data. - - """ - raise NotImplementedError - - @abc.abstractmethod - def fold(self, name, value): - """Given the header name and the value from the model, return a string - containing linesep characters that implement the folding of the header - according to the policy controls. The value passed in by the email - package may contain surrogateescaped binary data if the lines were - parsed by a BytesParser. The returned value should not contain any - surrogateescaped data. - - """ - raise NotImplementedError - - @abc.abstractmethod - def fold_binary(self, name, value): - """Given the header name and the value from the model, return binary - data containing linesep characters that implement the folding of the - header according to the policy controls. The value passed in by the - email package may contain surrogateescaped binary data. - - """ - raise NotImplementedError - - -@_extend_docstrings -class Compat32(Policy): - - """+ - This particular policy is the backward compatibility Policy. It - replicates the behavior of the email package version 5.1. - """ - - def _sanitize_header(self, name, value): - # If the header value contains surrogates, return a Header using - # the unknown-8bit charset to encode the bytes as encoded words. - if not isinstance(value, str): - # Assume it is already a header object - return value - if _has_surrogates(value): - return header.Header(value, charset=_charset.UNKNOWN8BIT, - header_name=name) - else: - return value - - def header_source_parse(self, sourcelines): - """+ - The name is parsed as everything up to the ':' and returned unmodified. - The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and - stripping any trailing carriage return or linefeed characters. - - """ - name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) - return (name, value.rstrip('\r\n')) - - def header_store_parse(self, name, value): - """+ - The name and value are returned unmodified. - """ - return (name, value) - - def header_fetch_parse(self, name, value): - """+ - If the value contains binary data, it is converted into a Header object - using the unknown-8bit charset. Otherwise it is returned unmodified. - """ - return self._sanitize_header(name, value) - - def fold(self, name, value): - """+ - Headers are folded using the Header folding algorithm, which preserves - existing line breaks in the value, and wraps each resulting line to the - max_line_length. Non-ASCII binary data are CTE encoded using the - unknown-8bit charset. - - """ - return self._fold(name, value, sanitize=True) - - def fold_binary(self, name, value): - """+ - Headers are folded using the Header folding algorithm, which preserves - existing line breaks in the value, and wraps each resulting line to the - max_line_length. If cte_type is 7bit, non-ascii binary data is CTE - encoded using the unknown-8bit charset. Otherwise the original source - header is used, with its existing line breaks and/or binary data. - - """ - folded = self._fold(name, value, sanitize=self.cte_type=='7bit') - return folded.encode('ascii', 'surrogateescape') - - def _fold(self, name, value, sanitize): - parts = [] - parts.append('%s: ' % name) - if isinstance(value, str): - if _has_surrogates(value): - if sanitize: - h = header.Header(value, - charset=_charset.UNKNOWN8BIT, - header_name=name) - else: - # If we have raw 8bit data in a byte string, we have no idea - # what the encoding is. There is no safe way to split this - # string. If it's ascii-subset, then we could do a normal - # ascii split, but if it's multibyte then we could break the - # string. There's no way to know so the least harm seems to - # be to not split the string and risk it being too long. - parts.append(value) - h = None - else: - h = header.Header(value, header_name=name) - else: - # Assume it is a Header-like object. - h = value - if h is not None: - parts.append(h.encode(linesep=self.linesep, - maxlinelen=self.max_line_length)) - parts.append(self.linesep) - return ''.join(parts) - - -compat32 = Compat32() diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/policy.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/policy.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/policy.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/policy.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,193 +0,0 @@ -"""This will be the home for the policy that hooks in the new -code that adds all the email6 features. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import super - -from future.standard_library.email._policybase import (Policy, Compat32, - compat32, _extend_docstrings) -from future.standard_library.email.utils import _has_surrogates -from future.standard_library.email.headerregistry import HeaderRegistry as HeaderRegistry - -__all__ = [ - 'Compat32', - 'compat32', - 'Policy', - 'EmailPolicy', - 'default', - 'strict', - 'SMTP', - 'HTTP', - ] - -@_extend_docstrings -class EmailPolicy(Policy): - - """+ - PROVISIONAL - - The API extensions enabled by this policy are currently provisional. - Refer to the documentation for details. - - This policy adds new header parsing and folding algorithms. Instead of - simple strings, headers are custom objects with custom attributes - depending on the type of the field. The folding algorithm fully - implements RFCs 2047 and 5322. - - In addition to the settable attributes listed above that apply to - all Policies, this policy adds the following additional attributes: - - refold_source -- if the value for a header in the Message object - came from the parsing of some source, this attribute - indicates whether or not a generator should refold - that value when transforming the message back into - stream form. The possible values are: - - none -- all source values use original folding - long -- source values that have any line that is - longer than max_line_length will be - refolded - all -- all values are refolded. - - The default is 'long'. - - header_factory -- a callable that takes two arguments, 'name' and - 'value', where 'name' is a header field name and - 'value' is an unfolded header field value, and - returns a string-like object that represents that - header. A default header_factory is provided that - understands some of the RFC5322 header field types. - (Currently address fields and date fields have - special treatment, while all other fields are - treated as unstructured. This list will be - completed before the extension is marked stable.) - """ - - refold_source = 'long' - header_factory = HeaderRegistry() - - def __init__(self, **kw): - # Ensure that each new instance gets a unique header factory - # (as opposed to clones, which share the factory). - if 'header_factory' not in kw: - object.__setattr__(self, 'header_factory', HeaderRegistry()) - super().__init__(**kw) - - def header_max_count(self, name): - """+ - The implementation for this class returns the max_count attribute from - the specialized header class that would be used to construct a header - of type 'name'. - """ - return self.header_factory[name].max_count - - # The logic of the next three methods is chosen such that it is possible to - # switch a Message object between a Compat32 policy and a policy derived - # from this class and have the results stay consistent. This allows a - # Message object constructed with this policy to be passed to a library - # that only handles Compat32 objects, or to receive such an object and - # convert it to use the newer style by just changing its policy. It is - # also chosen because it postpones the relatively expensive full rfc5322 - # parse until as late as possible when parsing from source, since in many - # applications only a few headers will actually be inspected. - - def header_source_parse(self, sourcelines): - """+ - The name is parsed as everything up to the ':' and returned unmodified. - The value is determined by stripping leading whitespace off the - remainder of the first line, joining all subsequent lines together, and - stripping any trailing carriage return or linefeed characters. (This - is the same as Compat32). - - """ - name, value = sourcelines[0].split(':', 1) - value = value.lstrip(' \t') + ''.join(sourcelines[1:]) - return (name, value.rstrip('\r\n')) - - def header_store_parse(self, name, value): - """+ - The name is returned unchanged. If the input value has a 'name' - attribute and it matches the name ignoring case, the value is returned - unchanged. Otherwise the name and value are passed to header_factory - method, and the resulting custom header object is returned as the - value. In this case a ValueError is raised if the input value contains - CR or LF characters. - - """ - if hasattr(value, 'name') and value.name.lower() == name.lower(): - return (name, value) - if isinstance(value, str) and len(value.splitlines())>1: - raise ValueError("Header values may not contain linefeed " - "or carriage return characters") - return (name, self.header_factory(name, value)) - - def header_fetch_parse(self, name, value): - """+ - If the value has a 'name' attribute, it is returned to unmodified. - Otherwise the name and the value with any linesep characters removed - are passed to the header_factory method, and the resulting custom - header object is returned. Any surrogateescaped bytes get turned - into the unicode unknown-character glyph. - - """ - if hasattr(value, 'name'): - return value - return self.header_factory(name, ''.join(value.splitlines())) - - def fold(self, name, value): - """+ - Header folding is controlled by the refold_source policy setting. A - value is considered to be a 'source value' if and only if it does not - have a 'name' attribute (having a 'name' attribute means it is a header - object of some sort). If a source value needs to be refolded according - to the policy, it is converted into a custom header object by passing - the name and the value with any linesep characters removed to the - header_factory method. Folding of a custom header object is done by - calling its fold method with the current policy. - - Source values are split into lines using splitlines. If the value is - not to be refolded, the lines are rejoined using the linesep from the - policy and returned. The exception is lines containing non-ascii - binary data. In that case the value is refolded regardless of the - refold_source setting, which causes the binary data to be CTE encoded - using the unknown-8bit charset. - - """ - return self._fold(name, value, refold_binary=True) - - def fold_binary(self, name, value): - """+ - The same as fold if cte_type is 7bit, except that the returned value is - bytes. - - If cte_type is 8bit, non-ASCII binary data is converted back into - bytes. Headers with binary data are not refolded, regardless of the - refold_header setting, since there is no way to know whether the binary - data consists of single byte characters or multibyte characters. - - """ - folded = self._fold(name, value, refold_binary=self.cte_type=='7bit') - return folded.encode('ascii', 'surrogateescape') - - def _fold(self, name, value, refold_binary=False): - if hasattr(value, 'name'): - return value.fold(policy=self) - maxlen = self.max_line_length if self.max_line_length else float('inf') - lines = value.splitlines() - refold = (self.refold_source == 'all' or - self.refold_source == 'long' and - (lines and len(lines[0])+len(name)+2 > maxlen or - any(len(x) > maxlen for x in lines[1:]))) - if refold or refold_binary and _has_surrogates(value): - return self.header_factory(name, ''.join(lines)).fold(policy=self) - return name + ': ' + self.linesep.join(lines) + self.linesep - - -default = EmailPolicy() -# Make the default policy use the class default header_factory -del default.header_factory -strict = default.clone(raise_on_defect=True) -SMTP = default.clone(linesep='\r\n') -HTTP = default.clone(linesep='\r\n', max_line_length=None) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/quoprimime.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/quoprimime.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/quoprimime.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/quoprimime.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,326 +0,0 @@ -# Copyright (C) 2001-2006 Python Software Foundation -# Author: Ben Gertzfield -# Contact: email-sig@python.org - -"""Quoted-printable content transfer encoding per RFCs 2045-2047. - -This module handles the content transfer encoding method defined in RFC 2045 -to encode US ASCII-like 8-bit data called `quoted-printable'. It is used to -safely encode text that is in a character set similar to the 7-bit US ASCII -character set, but that includes some 8-bit characters that are normally not -allowed in email bodies or headers. - -Quoted-printable is very space-inefficient for encoding binary files; use the -email.base64mime module for that instead. - -This module provides an interface to encode and decode both headers and bodies -with quoted-printable encoding. - -RFC 2045 defines a method for including character set information in an -`encoded-word' in a header. This method is commonly used for 8-bit real names -in To:/From:/Cc: etc. fields, as well as Subject: lines. - -This module does not do the line wrapping or end-of-line character -conversion necessary for proper internationalized headers; it only -does dumb encoding and decoding. To deal with the various line -wrapping issues, use the email.header module. -""" -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future.builtins import bytes, chr, dict, int, range, super - -__all__ = [ - 'body_decode', - 'body_encode', - 'body_length', - 'decode', - 'decodestring', - 'header_decode', - 'header_encode', - 'header_length', - 'quote', - 'unquote', - ] - -import re -import io - -from string import ascii_letters, digits, hexdigits - -CRLF = '\r\n' -NL = '\n' -EMPTYSTRING = '' - -# Build a mapping of octets to the expansion of that octet. Since we're only -# going to have 256 of these things, this isn't terribly inefficient -# space-wise. Remember that headers and bodies have different sets of safe -# characters. Initialize both maps with the full expansion, and then override -# the safe bytes with the more compact form. -_QUOPRI_HEADER_MAP = dict((c, '=%02X' % c) for c in range(256)) -_QUOPRI_BODY_MAP = _QUOPRI_HEADER_MAP.copy() - -# Safe header bytes which need no encoding. -for c in bytes(b'-!*+/' + ascii_letters.encode('ascii') + digits.encode('ascii')): - _QUOPRI_HEADER_MAP[c] = chr(c) -# Headers have one other special encoding; spaces become underscores. -_QUOPRI_HEADER_MAP[ord(' ')] = '_' - -# Safe body bytes which need no encoding. -for c in bytes(b' !"#$%&\'()*+,-./0123456789:;<>' - b'?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`' - b'abcdefghijklmnopqrstuvwxyz{|}~\t'): - _QUOPRI_BODY_MAP[c] = chr(c) - - - -# Helpers -def header_check(octet): - """Return True if the octet should be escaped with header quopri.""" - return chr(octet) != _QUOPRI_HEADER_MAP[octet] - - -def body_check(octet): - """Return True if the octet should be escaped with body quopri.""" - return chr(octet) != _QUOPRI_BODY_MAP[octet] - - -def header_length(bytearray): - """Return a header quoted-printable encoding length. - - Note that this does not include any RFC 2047 chrome added by - `header_encode()`. - - :param bytearray: An array of bytes (a.k.a. octets). - :return: The length in bytes of the byte array when it is encoded with - quoted-printable for headers. - """ - return sum(len(_QUOPRI_HEADER_MAP[octet]) for octet in bytearray) - - -def body_length(bytearray): - """Return a body quoted-printable encoding length. - - :param bytearray: An array of bytes (a.k.a. octets). - :return: The length in bytes of the byte array when it is encoded with - quoted-printable for bodies. - """ - return sum(len(_QUOPRI_BODY_MAP[octet]) for octet in bytearray) - - -def _max_append(L, s, maxlen, extra=''): - if not isinstance(s, str): - s = chr(s) - if not L: - L.append(s.lstrip()) - elif len(L[-1]) + len(s) <= maxlen: - L[-1] += extra + s - else: - L.append(s.lstrip()) - - -def unquote(s): - """Turn a string in the form =AB to the ASCII character with value 0xab""" - return chr(int(s[1:3], 16)) - - -def quote(c): - return '=%02X' % ord(c) - - - -def header_encode(header_bytes, charset='iso-8859-1'): - """Encode a single header line with quoted-printable (like) encoding. - - Defined in RFC 2045, this `Q' encoding is similar to quoted-printable, but - used specifically for email header fields to allow charsets with mostly 7 - bit characters (and some 8 bit) to remain more or less readable in non-RFC - 2045 aware mail clients. - - charset names the character set to use in the RFC 2046 header. It - defaults to iso-8859-1. - """ - # Return empty headers as an empty string. - if not header_bytes: - return '' - # Iterate over every byte, encoding if necessary. - encoded = [] - for octet in header_bytes: - encoded.append(_QUOPRI_HEADER_MAP[octet]) - # Now add the RFC chrome to each encoded chunk and glue the chunks - # together. - return '=?%s?q?%s?=' % (charset, EMPTYSTRING.join(encoded)) - - -class _body_accumulator(io.StringIO): - - def __init__(self, maxlinelen, eol, *args, **kw): - super().__init__(*args, **kw) - self.eol = eol - self.maxlinelen = self.room = maxlinelen - - def write_str(self, s): - """Add string s to the accumulated body.""" - self.write(s) - self.room -= len(s) - - def newline(self): - """Write eol, then start new line.""" - self.write_str(self.eol) - self.room = self.maxlinelen - - def write_soft_break(self): - """Write a soft break, then start a new line.""" - self.write_str('=') - self.newline() - - def write_wrapped(self, s, extra_room=0): - """Add a soft line break if needed, then write s.""" - if self.room < len(s) + extra_room: - self.write_soft_break() - self.write_str(s) - - def write_char(self, c, is_last_char): - if not is_last_char: - # Another character follows on this line, so we must leave - # extra room, either for it or a soft break, and whitespace - # need not be quoted. - self.write_wrapped(c, extra_room=1) - elif c not in ' \t': - # For this and remaining cases, no more characters follow, - # so there is no need to reserve extra room (since a hard - # break will immediately follow). - self.write_wrapped(c) - elif self.room >= 3: - # It's a whitespace character at end-of-line, and we have room - # for the three-character quoted encoding. - self.write(quote(c)) - elif self.room == 2: - # There's room for the whitespace character and a soft break. - self.write(c) - self.write_soft_break() - else: - # There's room only for a soft break. The quoted whitespace - # will be the only content on the subsequent line. - self.write_soft_break() - self.write(quote(c)) - - -def body_encode(body, maxlinelen=76, eol=NL): - """Encode with quoted-printable, wrapping at maxlinelen characters. - - Each line of encoded text will end with eol, which defaults to "\\n". Set - this to "\\r\\n" if you will be using the result of this function directly - in an email. - - Each line will be wrapped at, at most, maxlinelen characters before the - eol string (maxlinelen defaults to 76 characters, the maximum value - permitted by RFC 2045). Long lines will have the 'soft line break' - quoted-printable character "=" appended to them, so the decoded text will - be identical to the original text. - - The minimum maxlinelen is 4 to have room for a quoted character ("=XX") - followed by a soft line break. Smaller values will generate a - ValueError. - - """ - - if maxlinelen < 4: - raise ValueError("maxlinelen must be at least 4") - if not body: - return body - - # The last line may or may not end in eol, but all other lines do. - last_has_eol = (body[-1] in '\r\n') - - # This accumulator will make it easier to build the encoded body. - encoded_body = _body_accumulator(maxlinelen, eol) - - lines = body.splitlines() - last_line_no = len(lines) - 1 - for line_no, line in enumerate(lines): - last_char_index = len(line) - 1 - for i, c in enumerate(line): - if body_check(ord(c)): - c = quote(c) - encoded_body.write_char(c, i==last_char_index) - # Add an eol if input line had eol. All input lines have eol except - # possibly the last one. - if line_no < last_line_no or last_has_eol: - encoded_body.newline() - - return encoded_body.getvalue() - - - -# BAW: I'm not sure if the intent was for the signature of this function to be -# the same as base64MIME.decode() or not... -def decode(encoded, eol=NL): - """Decode a quoted-printable string. - - Lines are separated with eol, which defaults to \\n. - """ - if not encoded: - return encoded - # BAW: see comment in encode() above. Again, we're building up the - # decoded string with string concatenation, which could be done much more - # efficiently. - decoded = '' - - for line in encoded.splitlines(): - line = line.rstrip() - if not line: - decoded += eol - continue - - i = 0 - n = len(line) - while i < n: - c = line[i] - if c != '=': - decoded += c - i += 1 - # Otherwise, c == "=". Are we at the end of the line? If so, add - # a soft line break. - elif i+1 == n: - i += 1 - continue - # Decode if in form =AB - elif i+2 < n and line[i+1] in hexdigits and line[i+2] in hexdigits: - decoded += unquote(line[i:i+3]) - i += 3 - # Otherwise, not in form =AB, pass literally - else: - decoded += c - i += 1 - - if i == n: - decoded += eol - # Special case if original string did not end with eol - if encoded[-1] not in '\r\n' and decoded.endswith(eol): - decoded = decoded[:-1] - return decoded - - -# For convenience and backwards compatibility w/ standard base64 module -body_decode = decode -decodestring = decode - - - -def _unquote_match(match): - """Turn a match in the form =AB to the ASCII character with value 0xab""" - s = match.group(0) - return unquote(s) - - -# Header decoding is done a bit differently -def header_decode(s): - """Decode a string encoded with RFC 2045 MIME header `Q' encoding. - - This function does not parse a full MIME header value encoded with - quoted-printable (like =?iso-8895-1?q?Hello_World?=) -- please use - the high level email.header class for that functionality. - """ - s = s.replace('_', ' ') - return re.sub(r'=[a-fA-F0-9]{2}', _unquote_match, s, re.ASCII) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/utils.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/utils.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/email/utils.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/email/utils.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,400 +0,0 @@ -# Copyright (C) 2001-2010 Python Software Foundation -# Author: Barry Warsaw -# Contact: email-sig@python.org - -"""Miscellaneous utilities.""" - -from __future__ import unicode_literals -from __future__ import division -from __future__ import absolute_import -from future import utils -from future.builtins import bytes, int, str - -__all__ = [ - 'collapse_rfc2231_value', - 'decode_params', - 'decode_rfc2231', - 'encode_rfc2231', - 'formataddr', - 'formatdate', - 'format_datetime', - 'getaddresses', - 'make_msgid', - 'mktime_tz', - 'parseaddr', - 'parsedate', - 'parsedate_tz', - 'parsedate_to_datetime', - 'unquote', - ] - -import os -import re -if utils.PY2: - re.ASCII = 0 -import time -import base64 -import random -import socket -from future.backports import datetime -from future.backports.urllib.parse import quote as url_quote, unquote as url_unquote -import warnings -from io import StringIO - -from future.backports.email._parseaddr import quote -from future.backports.email._parseaddr import AddressList as _AddressList -from future.backports.email._parseaddr import mktime_tz - -from future.backports.email._parseaddr import parsedate, parsedate_tz, _parsedate_tz - -from quopri import decodestring as _qdecode - -# Intrapackage imports -from future.backports.email.encoders import _bencode, _qencode -from future.backports.email.charset import Charset - -COMMASPACE = ', ' -EMPTYSTRING = '' -UEMPTYSTRING = '' -CRLF = '\r\n' -TICK = "'" - -specialsre = re.compile(r'[][\\()<>@,:;".]') -escapesre = re.compile(r'[\\"]') - -# How to figure out if we are processing strings that come from a byte -# source with undecodable characters. -_has_surrogates = re.compile( - '([^\ud800-\udbff]|\A)[\udc00-\udfff]([^\udc00-\udfff]|\Z)').search - -# How to deal with a string containing bytes before handing it to the -# application through the 'normal' interface. -def _sanitize(string): - # Turn any escaped bytes into unicode 'unknown' char. - original_bytes = string.encode('ascii', 'surrogateescape') - return original_bytes.decode('ascii', 'replace') - - -# Helpers - -def formataddr(pair, charset='utf-8'): - """The inverse of parseaddr(), this takes a 2-tuple of the form - (realname, email_address) and returns the string value suitable - for an RFC 2822 From, To or Cc header. - - If the first element of pair is false, then the second element is - returned unmodified. - - Optional charset if given is the character set that is used to encode - realname in case realname is not ASCII safe. Can be an instance of str or - a Charset-like object which has a header_encode method. Default is - 'utf-8'. - """ - name, address = pair - # The address MUST (per RFC) be ascii, so raise an UnicodeError if it isn't. - address.encode('ascii') - if name: - try: - name.encode('ascii') - except UnicodeEncodeError: - if isinstance(charset, str): - charset = Charset(charset) - encoded_name = charset.header_encode(name) - return "%s <%s>" % (encoded_name, address) - else: - quotes = '' - if specialsre.search(name): - quotes = '"' - name = escapesre.sub(r'\\\g<0>', name) - return '%s%s%s <%s>' % (quotes, name, quotes, address) - return address - - - -def getaddresses(fieldvalues): - """Return a list of (REALNAME, EMAIL) for each fieldvalue.""" - all = COMMASPACE.join(fieldvalues) - a = _AddressList(all) - return a.addresslist - - - -ecre = re.compile(r''' - =\? # literal =? - (?P[^?]*?) # non-greedy up to the next ? is the charset - \? # literal ? - (?P[qb]) # either a "q" or a "b", case insensitive - \? # literal ? - (?P.*?) # non-greedy up to the next ?= is the atom - \?= # literal ?= - ''', re.VERBOSE | re.IGNORECASE) - - -def _format_timetuple_and_zone(timetuple, zone): - return '%s, %02d %s %04d %02d:%02d:%02d %s' % ( - ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'][timetuple[6]], - timetuple[2], - ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'][timetuple[1] - 1], - timetuple[0], timetuple[3], timetuple[4], timetuple[5], - zone) - -def formatdate(timeval=None, localtime=False, usegmt=False): - """Returns a date string as specified by RFC 2822, e.g.: - - Fri, 09 Nov 2001 01:08:47 -0000 - - Optional timeval if given is a floating point time value as accepted by - gmtime() and localtime(), otherwise the current time is used. - - Optional localtime is a flag that when True, interprets timeval, and - returns a date relative to the local timezone instead of UTC, properly - taking daylight savings time into account. - - Optional argument usegmt means that the timezone is written out as - an ascii string, not numeric one (so "GMT" instead of "+0000"). This - is needed for HTTP, and is only used when localtime==False. - """ - # Note: we cannot use strftime() because that honors the locale and RFC - # 2822 requires that day and month names be the English abbreviations. - if timeval is None: - timeval = time.time() - if localtime: - now = time.localtime(timeval) - # Calculate timezone offset, based on whether the local zone has - # daylight savings time, and whether DST is in effect. - if time.daylight and now[-1]: - offset = time.altzone - else: - offset = time.timezone - hours, minutes = divmod(abs(offset), 3600) - # Remember offset is in seconds west of UTC, but the timezone is in - # minutes east of UTC, so the signs differ. - if offset > 0: - sign = '-' - else: - sign = '+' - zone = '%s%02d%02d' % (sign, hours, minutes // 60) - else: - now = time.gmtime(timeval) - # Timezone offset is always -0000 - if usegmt: - zone = 'GMT' - else: - zone = '-0000' - return _format_timetuple_and_zone(now, zone) - -def format_datetime(dt, usegmt=False): - """Turn a datetime into a date string as specified in RFC 2822. - - If usegmt is True, dt must be an aware datetime with an offset of zero. In - this case 'GMT' will be rendered instead of the normal +0000 required by - RFC2822. This is to support HTTP headers involving date stamps. - """ - now = dt.timetuple() - if usegmt: - if dt.tzinfo is None or dt.tzinfo != datetime.timezone.utc: - raise ValueError("usegmt option requires a UTC datetime") - zone = 'GMT' - elif dt.tzinfo is None: - zone = '-0000' - else: - zone = dt.strftime("%z") - return _format_timetuple_and_zone(now, zone) - - -def make_msgid(idstring=None, domain=None): - """Returns a string suitable for RFC 2822 compliant Message-ID, e.g: - - <20020201195627.33539.96671@nightshade.la.mastaler.com> - - Optional idstring if given is a string used to strengthen the - uniqueness of the message id. Optional domain if given provides the - portion of the message id after the '@'. It defaults to the locally - defined hostname. - """ - timeval = time.time() - utcdate = time.strftime('%Y%m%d%H%M%S', time.gmtime(timeval)) - pid = os.getpid() - randint = random.randrange(100000) - if idstring is None: - idstring = '' - else: - idstring = '.' + idstring - if domain is None: - domain = socket.getfqdn() - msgid = '<%s.%s.%s%s@%s>' % (utcdate, pid, randint, idstring, domain) - return msgid - - -def parsedate_to_datetime(data): - _3to2list = list(_parsedate_tz(data)) - dtuple, tz, = [_3to2list[:-1]] + _3to2list[-1:] - if tz is None: - return datetime.datetime(*dtuple[:6]) - return datetime.datetime(*dtuple[:6], - tzinfo=datetime.timezone(datetime.timedelta(seconds=tz))) - - -def parseaddr(addr): - addrs = _AddressList(addr).addresslist - if not addrs: - return '', '' - return addrs[0] - - -# rfc822.unquote() doesn't properly de-backslash-ify in Python pre-2.3. -def unquote(str): - """Remove quotes from a string.""" - if len(str) > 1: - if str.startswith('"') and str.endswith('"'): - return str[1:-1].replace('\\\\', '\\').replace('\\"', '"') - if str.startswith('<') and str.endswith('>'): - return str[1:-1] - return str - - - -# RFC2231-related functions - parameter encoding and decoding -def decode_rfc2231(s): - """Decode string according to RFC 2231""" - parts = s.split(TICK, 2) - if len(parts) <= 2: - return None, None, s - return parts - - -def encode_rfc2231(s, charset=None, language=None): - """Encode string according to RFC 2231. - - If neither charset nor language is given, then s is returned as-is. If - charset is given but not language, the string is encoded using the empty - string for language. - """ - s = url_quote(s, safe='', encoding=charset or 'ascii') - if charset is None and language is None: - return s - if language is None: - language = '' - return "%s'%s'%s" % (charset, language, s) - - -rfc2231_continuation = re.compile(r'^(?P\w+)\*((?P[0-9]+)\*?)?$', - re.ASCII) - -def decode_params(params): - """Decode parameters list according to RFC 2231. - - params is a sequence of 2-tuples containing (param name, string value). - """ - # Copy params so we don't mess with the original - params = params[:] - new_params = [] - # Map parameter's name to a list of continuations. The values are a - # 3-tuple of the continuation number, the string value, and a flag - # specifying whether a particular segment is %-encoded. - rfc2231_params = {} - name, value = params.pop(0) - new_params.append((name, value)) - while params: - name, value = params.pop(0) - if name.endswith('*'): - encoded = True - else: - encoded = False - value = unquote(value) - mo = rfc2231_continuation.match(name) - if mo: - name, num = mo.group('name', 'num') - if num is not None: - num = int(num) - rfc2231_params.setdefault(name, []).append((num, value, encoded)) - else: - new_params.append((name, '"%s"' % quote(value))) - if rfc2231_params: - for name, continuations in rfc2231_params.items(): - value = [] - extended = False - # Sort by number - continuations.sort() - # And now append all values in numerical order, converting - # %-encodings for the encoded segments. If any of the - # continuation names ends in a *, then the entire string, after - # decoding segments and concatenating, must have the charset and - # language specifiers at the beginning of the string. - for num, s, encoded in continuations: - if encoded: - # Decode as "latin-1", so the characters in s directly - # represent the percent-encoded octet values. - # collapse_rfc2231_value treats this as an octet sequence. - s = url_unquote(s, encoding="latin-1") - extended = True - value.append(s) - value = quote(EMPTYSTRING.join(value)) - if extended: - charset, language, value = decode_rfc2231(value) - new_params.append((name, (charset, language, '"%s"' % value))) - else: - new_params.append((name, '"%s"' % value)) - return new_params - -def collapse_rfc2231_value(value, errors='replace', - fallback_charset='us-ascii'): - if not isinstance(value, tuple) or len(value) != 3: - return unquote(value) - # While value comes to us as a unicode string, we need it to be a bytes - # object. We do not want bytes() normal utf-8 decoder, we want a straight - # interpretation of the string as character bytes. - charset, language, text = value - rawbytes = bytes(text, 'raw-unicode-escape') - try: - return str(rawbytes, charset, errors) - except LookupError: - # charset is not a known codec. - return unquote(text) - - -# -# datetime doesn't provide a localtime function yet, so provide one. Code -# adapted from the patch in issue 9527. This may not be perfect, but it is -# better than not having it. -# - -def localtime(dt=None, isdst=-1): - """Return local time as an aware datetime object. - - If called without arguments, return current time. Otherwise *dt* - argument should be a datetime instance, and it is converted to the - local time zone according to the system time zone database. If *dt* is - naive (that is, dt.tzinfo is None), it is assumed to be in local time. - In this case, a positive or zero value for *isdst* causes localtime to - presume initially that summer time (for example, Daylight Saving Time) - is or is not (respectively) in effect for the specified time. A - negative value for *isdst* causes the localtime() function to attempt - to divine whether summer time is in effect for the specified time. - - """ - if dt is None: - return datetime.datetime.now(datetime.timezone.utc).astimezone() - if dt.tzinfo is not None: - return dt.astimezone() - # We have a naive datetime. Convert to a (localtime) timetuple and pass to - # system mktime together with the isdst hint. System mktime will return - # seconds since epoch. - tm = dt.timetuple()[:-1] + (isdst,) - seconds = time.mktime(tm) - localtm = time.localtime(seconds) - try: - delta = datetime.timedelta(seconds=localtm.tm_gmtoff) - tz = datetime.timezone(delta, localtm.tm_zone) - except AttributeError: - # Compute UTC offset and compare with the value implied by tm_isdst. - # If the values match, use the zone name implied by tm_isdst. - delta = dt - datetime.datetime(*time.gmtime(seconds)[:6]) - dst = time.daylight and localtm.tm_isdst > 0 - gmtoff = -(time.altzone if dst else time.timezone) - if delta == datetime.timedelta(seconds=gmtoff): - tz = datetime.timezone(delta, time.tzname[dst]) - else: - tz = datetime.timezone(delta) - return dt.replace(tzinfo=tz) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/html/entities.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/html/entities.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/html/entities.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/html/entities.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2515 +0,0 @@ -"""HTML character entity references. - -Backported for python-future from Python 3.3 -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import * - - -# maps the HTML entity name to the Unicode codepoint -name2codepoint = { - 'AElig': 0x00c6, # latin capital letter AE = latin capital ligature AE, U+00C6 ISOlat1 - 'Aacute': 0x00c1, # latin capital letter A with acute, U+00C1 ISOlat1 - 'Acirc': 0x00c2, # latin capital letter A with circumflex, U+00C2 ISOlat1 - 'Agrave': 0x00c0, # latin capital letter A with grave = latin capital letter A grave, U+00C0 ISOlat1 - 'Alpha': 0x0391, # greek capital letter alpha, U+0391 - 'Aring': 0x00c5, # latin capital letter A with ring above = latin capital letter A ring, U+00C5 ISOlat1 - 'Atilde': 0x00c3, # latin capital letter A with tilde, U+00C3 ISOlat1 - 'Auml': 0x00c4, # latin capital letter A with diaeresis, U+00C4 ISOlat1 - 'Beta': 0x0392, # greek capital letter beta, U+0392 - 'Ccedil': 0x00c7, # latin capital letter C with cedilla, U+00C7 ISOlat1 - 'Chi': 0x03a7, # greek capital letter chi, U+03A7 - 'Dagger': 0x2021, # double dagger, U+2021 ISOpub - 'Delta': 0x0394, # greek capital letter delta, U+0394 ISOgrk3 - 'ETH': 0x00d0, # latin capital letter ETH, U+00D0 ISOlat1 - 'Eacute': 0x00c9, # latin capital letter E with acute, U+00C9 ISOlat1 - 'Ecirc': 0x00ca, # latin capital letter E with circumflex, U+00CA ISOlat1 - 'Egrave': 0x00c8, # latin capital letter E with grave, U+00C8 ISOlat1 - 'Epsilon': 0x0395, # greek capital letter epsilon, U+0395 - 'Eta': 0x0397, # greek capital letter eta, U+0397 - 'Euml': 0x00cb, # latin capital letter E with diaeresis, U+00CB ISOlat1 - 'Gamma': 0x0393, # greek capital letter gamma, U+0393 ISOgrk3 - 'Iacute': 0x00cd, # latin capital letter I with acute, U+00CD ISOlat1 - 'Icirc': 0x00ce, # latin capital letter I with circumflex, U+00CE ISOlat1 - 'Igrave': 0x00cc, # latin capital letter I with grave, U+00CC ISOlat1 - 'Iota': 0x0399, # greek capital letter iota, U+0399 - 'Iuml': 0x00cf, # latin capital letter I with diaeresis, U+00CF ISOlat1 - 'Kappa': 0x039a, # greek capital letter kappa, U+039A - 'Lambda': 0x039b, # greek capital letter lambda, U+039B ISOgrk3 - 'Mu': 0x039c, # greek capital letter mu, U+039C - 'Ntilde': 0x00d1, # latin capital letter N with tilde, U+00D1 ISOlat1 - 'Nu': 0x039d, # greek capital letter nu, U+039D - 'OElig': 0x0152, # latin capital ligature OE, U+0152 ISOlat2 - 'Oacute': 0x00d3, # latin capital letter O with acute, U+00D3 ISOlat1 - 'Ocirc': 0x00d4, # latin capital letter O with circumflex, U+00D4 ISOlat1 - 'Ograve': 0x00d2, # latin capital letter O with grave, U+00D2 ISOlat1 - 'Omega': 0x03a9, # greek capital letter omega, U+03A9 ISOgrk3 - 'Omicron': 0x039f, # greek capital letter omicron, U+039F - 'Oslash': 0x00d8, # latin capital letter O with stroke = latin capital letter O slash, U+00D8 ISOlat1 - 'Otilde': 0x00d5, # latin capital letter O with tilde, U+00D5 ISOlat1 - 'Ouml': 0x00d6, # latin capital letter O with diaeresis, U+00D6 ISOlat1 - 'Phi': 0x03a6, # greek capital letter phi, U+03A6 ISOgrk3 - 'Pi': 0x03a0, # greek capital letter pi, U+03A0 ISOgrk3 - 'Prime': 0x2033, # double prime = seconds = inches, U+2033 ISOtech - 'Psi': 0x03a8, # greek capital letter psi, U+03A8 ISOgrk3 - 'Rho': 0x03a1, # greek capital letter rho, U+03A1 - 'Scaron': 0x0160, # latin capital letter S with caron, U+0160 ISOlat2 - 'Sigma': 0x03a3, # greek capital letter sigma, U+03A3 ISOgrk3 - 'THORN': 0x00de, # latin capital letter THORN, U+00DE ISOlat1 - 'Tau': 0x03a4, # greek capital letter tau, U+03A4 - 'Theta': 0x0398, # greek capital letter theta, U+0398 ISOgrk3 - 'Uacute': 0x00da, # latin capital letter U with acute, U+00DA ISOlat1 - 'Ucirc': 0x00db, # latin capital letter U with circumflex, U+00DB ISOlat1 - 'Ugrave': 0x00d9, # latin capital letter U with grave, U+00D9 ISOlat1 - 'Upsilon': 0x03a5, # greek capital letter upsilon, U+03A5 ISOgrk3 - 'Uuml': 0x00dc, # latin capital letter U with diaeresis, U+00DC ISOlat1 - 'Xi': 0x039e, # greek capital letter xi, U+039E ISOgrk3 - 'Yacute': 0x00dd, # latin capital letter Y with acute, U+00DD ISOlat1 - 'Yuml': 0x0178, # latin capital letter Y with diaeresis, U+0178 ISOlat2 - 'Zeta': 0x0396, # greek capital letter zeta, U+0396 - 'aacute': 0x00e1, # latin small letter a with acute, U+00E1 ISOlat1 - 'acirc': 0x00e2, # latin small letter a with circumflex, U+00E2 ISOlat1 - 'acute': 0x00b4, # acute accent = spacing acute, U+00B4 ISOdia - 'aelig': 0x00e6, # latin small letter ae = latin small ligature ae, U+00E6 ISOlat1 - 'agrave': 0x00e0, # latin small letter a with grave = latin small letter a grave, U+00E0 ISOlat1 - 'alefsym': 0x2135, # alef symbol = first transfinite cardinal, U+2135 NEW - 'alpha': 0x03b1, # greek small letter alpha, U+03B1 ISOgrk3 - 'amp': 0x0026, # ampersand, U+0026 ISOnum - 'and': 0x2227, # logical and = wedge, U+2227 ISOtech - 'ang': 0x2220, # angle, U+2220 ISOamso - 'aring': 0x00e5, # latin small letter a with ring above = latin small letter a ring, U+00E5 ISOlat1 - 'asymp': 0x2248, # almost equal to = asymptotic to, U+2248 ISOamsr - 'atilde': 0x00e3, # latin small letter a with tilde, U+00E3 ISOlat1 - 'auml': 0x00e4, # latin small letter a with diaeresis, U+00E4 ISOlat1 - 'bdquo': 0x201e, # double low-9 quotation mark, U+201E NEW - 'beta': 0x03b2, # greek small letter beta, U+03B2 ISOgrk3 - 'brvbar': 0x00a6, # broken bar = broken vertical bar, U+00A6 ISOnum - 'bull': 0x2022, # bullet = black small circle, U+2022 ISOpub - 'cap': 0x2229, # intersection = cap, U+2229 ISOtech - 'ccedil': 0x00e7, # latin small letter c with cedilla, U+00E7 ISOlat1 - 'cedil': 0x00b8, # cedilla = spacing cedilla, U+00B8 ISOdia - 'cent': 0x00a2, # cent sign, U+00A2 ISOnum - 'chi': 0x03c7, # greek small letter chi, U+03C7 ISOgrk3 - 'circ': 0x02c6, # modifier letter circumflex accent, U+02C6 ISOpub - 'clubs': 0x2663, # black club suit = shamrock, U+2663 ISOpub - 'cong': 0x2245, # approximately equal to, U+2245 ISOtech - 'copy': 0x00a9, # copyright sign, U+00A9 ISOnum - 'crarr': 0x21b5, # downwards arrow with corner leftwards = carriage return, U+21B5 NEW - 'cup': 0x222a, # union = cup, U+222A ISOtech - 'curren': 0x00a4, # currency sign, U+00A4 ISOnum - 'dArr': 0x21d3, # downwards double arrow, U+21D3 ISOamsa - 'dagger': 0x2020, # dagger, U+2020 ISOpub - 'darr': 0x2193, # downwards arrow, U+2193 ISOnum - 'deg': 0x00b0, # degree sign, U+00B0 ISOnum - 'delta': 0x03b4, # greek small letter delta, U+03B4 ISOgrk3 - 'diams': 0x2666, # black diamond suit, U+2666 ISOpub - 'divide': 0x00f7, # division sign, U+00F7 ISOnum - 'eacute': 0x00e9, # latin small letter e with acute, U+00E9 ISOlat1 - 'ecirc': 0x00ea, # latin small letter e with circumflex, U+00EA ISOlat1 - 'egrave': 0x00e8, # latin small letter e with grave, U+00E8 ISOlat1 - 'empty': 0x2205, # empty set = null set = diameter, U+2205 ISOamso - 'emsp': 0x2003, # em space, U+2003 ISOpub - 'ensp': 0x2002, # en space, U+2002 ISOpub - 'epsilon': 0x03b5, # greek small letter epsilon, U+03B5 ISOgrk3 - 'equiv': 0x2261, # identical to, U+2261 ISOtech - 'eta': 0x03b7, # greek small letter eta, U+03B7 ISOgrk3 - 'eth': 0x00f0, # latin small letter eth, U+00F0 ISOlat1 - 'euml': 0x00eb, # latin small letter e with diaeresis, U+00EB ISOlat1 - 'euro': 0x20ac, # euro sign, U+20AC NEW - 'exist': 0x2203, # there exists, U+2203 ISOtech - 'fnof': 0x0192, # latin small f with hook = function = florin, U+0192 ISOtech - 'forall': 0x2200, # for all, U+2200 ISOtech - 'frac12': 0x00bd, # vulgar fraction one half = fraction one half, U+00BD ISOnum - 'frac14': 0x00bc, # vulgar fraction one quarter = fraction one quarter, U+00BC ISOnum - 'frac34': 0x00be, # vulgar fraction three quarters = fraction three quarters, U+00BE ISOnum - 'frasl': 0x2044, # fraction slash, U+2044 NEW - 'gamma': 0x03b3, # greek small letter gamma, U+03B3 ISOgrk3 - 'ge': 0x2265, # greater-than or equal to, U+2265 ISOtech - 'gt': 0x003e, # greater-than sign, U+003E ISOnum - 'hArr': 0x21d4, # left right double arrow, U+21D4 ISOamsa - 'harr': 0x2194, # left right arrow, U+2194 ISOamsa - 'hearts': 0x2665, # black heart suit = valentine, U+2665 ISOpub - 'hellip': 0x2026, # horizontal ellipsis = three dot leader, U+2026 ISOpub - 'iacute': 0x00ed, # latin small letter i with acute, U+00ED ISOlat1 - 'icirc': 0x00ee, # latin small letter i with circumflex, U+00EE ISOlat1 - 'iexcl': 0x00a1, # inverted exclamation mark, U+00A1 ISOnum - 'igrave': 0x00ec, # latin small letter i with grave, U+00EC ISOlat1 - 'image': 0x2111, # blackletter capital I = imaginary part, U+2111 ISOamso - 'infin': 0x221e, # infinity, U+221E ISOtech - 'int': 0x222b, # integral, U+222B ISOtech - 'iota': 0x03b9, # greek small letter iota, U+03B9 ISOgrk3 - 'iquest': 0x00bf, # inverted question mark = turned question mark, U+00BF ISOnum - 'isin': 0x2208, # element of, U+2208 ISOtech - 'iuml': 0x00ef, # latin small letter i with diaeresis, U+00EF ISOlat1 - 'kappa': 0x03ba, # greek small letter kappa, U+03BA ISOgrk3 - 'lArr': 0x21d0, # leftwards double arrow, U+21D0 ISOtech - 'lambda': 0x03bb, # greek small letter lambda, U+03BB ISOgrk3 - 'lang': 0x2329, # left-pointing angle bracket = bra, U+2329 ISOtech - 'laquo': 0x00ab, # left-pointing double angle quotation mark = left pointing guillemet, U+00AB ISOnum - 'larr': 0x2190, # leftwards arrow, U+2190 ISOnum - 'lceil': 0x2308, # left ceiling = apl upstile, U+2308 ISOamsc - 'ldquo': 0x201c, # left double quotation mark, U+201C ISOnum - 'le': 0x2264, # less-than or equal to, U+2264 ISOtech - 'lfloor': 0x230a, # left floor = apl downstile, U+230A ISOamsc - 'lowast': 0x2217, # asterisk operator, U+2217 ISOtech - 'loz': 0x25ca, # lozenge, U+25CA ISOpub - 'lrm': 0x200e, # left-to-right mark, U+200E NEW RFC 2070 - 'lsaquo': 0x2039, # single left-pointing angle quotation mark, U+2039 ISO proposed - 'lsquo': 0x2018, # left single quotation mark, U+2018 ISOnum - 'lt': 0x003c, # less-than sign, U+003C ISOnum - 'macr': 0x00af, # macron = spacing macron = overline = APL overbar, U+00AF ISOdia - 'mdash': 0x2014, # em dash, U+2014 ISOpub - 'micro': 0x00b5, # micro sign, U+00B5 ISOnum - 'middot': 0x00b7, # middle dot = Georgian comma = Greek middle dot, U+00B7 ISOnum - 'minus': 0x2212, # minus sign, U+2212 ISOtech - 'mu': 0x03bc, # greek small letter mu, U+03BC ISOgrk3 - 'nabla': 0x2207, # nabla = backward difference, U+2207 ISOtech - 'nbsp': 0x00a0, # no-break space = non-breaking space, U+00A0 ISOnum - 'ndash': 0x2013, # en dash, U+2013 ISOpub - 'ne': 0x2260, # not equal to, U+2260 ISOtech - 'ni': 0x220b, # contains as member, U+220B ISOtech - 'not': 0x00ac, # not sign, U+00AC ISOnum - 'notin': 0x2209, # not an element of, U+2209 ISOtech - 'nsub': 0x2284, # not a subset of, U+2284 ISOamsn - 'ntilde': 0x00f1, # latin small letter n with tilde, U+00F1 ISOlat1 - 'nu': 0x03bd, # greek small letter nu, U+03BD ISOgrk3 - 'oacute': 0x00f3, # latin small letter o with acute, U+00F3 ISOlat1 - 'ocirc': 0x00f4, # latin small letter o with circumflex, U+00F4 ISOlat1 - 'oelig': 0x0153, # latin small ligature oe, U+0153 ISOlat2 - 'ograve': 0x00f2, # latin small letter o with grave, U+00F2 ISOlat1 - 'oline': 0x203e, # overline = spacing overscore, U+203E NEW - 'omega': 0x03c9, # greek small letter omega, U+03C9 ISOgrk3 - 'omicron': 0x03bf, # greek small letter omicron, U+03BF NEW - 'oplus': 0x2295, # circled plus = direct sum, U+2295 ISOamsb - 'or': 0x2228, # logical or = vee, U+2228 ISOtech - 'ordf': 0x00aa, # feminine ordinal indicator, U+00AA ISOnum - 'ordm': 0x00ba, # masculine ordinal indicator, U+00BA ISOnum - 'oslash': 0x00f8, # latin small letter o with stroke, = latin small letter o slash, U+00F8 ISOlat1 - 'otilde': 0x00f5, # latin small letter o with tilde, U+00F5 ISOlat1 - 'otimes': 0x2297, # circled times = vector product, U+2297 ISOamsb - 'ouml': 0x00f6, # latin small letter o with diaeresis, U+00F6 ISOlat1 - 'para': 0x00b6, # pilcrow sign = paragraph sign, U+00B6 ISOnum - 'part': 0x2202, # partial differential, U+2202 ISOtech - 'permil': 0x2030, # per mille sign, U+2030 ISOtech - 'perp': 0x22a5, # up tack = orthogonal to = perpendicular, U+22A5 ISOtech - 'phi': 0x03c6, # greek small letter phi, U+03C6 ISOgrk3 - 'pi': 0x03c0, # greek small letter pi, U+03C0 ISOgrk3 - 'piv': 0x03d6, # greek pi symbol, U+03D6 ISOgrk3 - 'plusmn': 0x00b1, # plus-minus sign = plus-or-minus sign, U+00B1 ISOnum - 'pound': 0x00a3, # pound sign, U+00A3 ISOnum - 'prime': 0x2032, # prime = minutes = feet, U+2032 ISOtech - 'prod': 0x220f, # n-ary product = product sign, U+220F ISOamsb - 'prop': 0x221d, # proportional to, U+221D ISOtech - 'psi': 0x03c8, # greek small letter psi, U+03C8 ISOgrk3 - 'quot': 0x0022, # quotation mark = APL quote, U+0022 ISOnum - 'rArr': 0x21d2, # rightwards double arrow, U+21D2 ISOtech - 'radic': 0x221a, # square root = radical sign, U+221A ISOtech - 'rang': 0x232a, # right-pointing angle bracket = ket, U+232A ISOtech - 'raquo': 0x00bb, # right-pointing double angle quotation mark = right pointing guillemet, U+00BB ISOnum - 'rarr': 0x2192, # rightwards arrow, U+2192 ISOnum - 'rceil': 0x2309, # right ceiling, U+2309 ISOamsc - 'rdquo': 0x201d, # right double quotation mark, U+201D ISOnum - 'real': 0x211c, # blackletter capital R = real part symbol, U+211C ISOamso - 'reg': 0x00ae, # registered sign = registered trade mark sign, U+00AE ISOnum - 'rfloor': 0x230b, # right floor, U+230B ISOamsc - 'rho': 0x03c1, # greek small letter rho, U+03C1 ISOgrk3 - 'rlm': 0x200f, # right-to-left mark, U+200F NEW RFC 2070 - 'rsaquo': 0x203a, # single right-pointing angle quotation mark, U+203A ISO proposed - 'rsquo': 0x2019, # right single quotation mark, U+2019 ISOnum - 'sbquo': 0x201a, # single low-9 quotation mark, U+201A NEW - 'scaron': 0x0161, # latin small letter s with caron, U+0161 ISOlat2 - 'sdot': 0x22c5, # dot operator, U+22C5 ISOamsb - 'sect': 0x00a7, # section sign, U+00A7 ISOnum - 'shy': 0x00ad, # soft hyphen = discretionary hyphen, U+00AD ISOnum - 'sigma': 0x03c3, # greek small letter sigma, U+03C3 ISOgrk3 - 'sigmaf': 0x03c2, # greek small letter final sigma, U+03C2 ISOgrk3 - 'sim': 0x223c, # tilde operator = varies with = similar to, U+223C ISOtech - 'spades': 0x2660, # black spade suit, U+2660 ISOpub - 'sub': 0x2282, # subset of, U+2282 ISOtech - 'sube': 0x2286, # subset of or equal to, U+2286 ISOtech - 'sum': 0x2211, # n-ary sumation, U+2211 ISOamsb - 'sup': 0x2283, # superset of, U+2283 ISOtech - 'sup1': 0x00b9, # superscript one = superscript digit one, U+00B9 ISOnum - 'sup2': 0x00b2, # superscript two = superscript digit two = squared, U+00B2 ISOnum - 'sup3': 0x00b3, # superscript three = superscript digit three = cubed, U+00B3 ISOnum - 'supe': 0x2287, # superset of or equal to, U+2287 ISOtech - 'szlig': 0x00df, # latin small letter sharp s = ess-zed, U+00DF ISOlat1 - 'tau': 0x03c4, # greek small letter tau, U+03C4 ISOgrk3 - 'there4': 0x2234, # therefore, U+2234 ISOtech - 'theta': 0x03b8, # greek small letter theta, U+03B8 ISOgrk3 - 'thetasym': 0x03d1, # greek small letter theta symbol, U+03D1 NEW - 'thinsp': 0x2009, # thin space, U+2009 ISOpub - 'thorn': 0x00fe, # latin small letter thorn with, U+00FE ISOlat1 - 'tilde': 0x02dc, # small tilde, U+02DC ISOdia - 'times': 0x00d7, # multiplication sign, U+00D7 ISOnum - 'trade': 0x2122, # trade mark sign, U+2122 ISOnum - 'uArr': 0x21d1, # upwards double arrow, U+21D1 ISOamsa - 'uacute': 0x00fa, # latin small letter u with acute, U+00FA ISOlat1 - 'uarr': 0x2191, # upwards arrow, U+2191 ISOnum - 'ucirc': 0x00fb, # latin small letter u with circumflex, U+00FB ISOlat1 - 'ugrave': 0x00f9, # latin small letter u with grave, U+00F9 ISOlat1 - 'uml': 0x00a8, # diaeresis = spacing diaeresis, U+00A8 ISOdia - 'upsih': 0x03d2, # greek upsilon with hook symbol, U+03D2 NEW - 'upsilon': 0x03c5, # greek small letter upsilon, U+03C5 ISOgrk3 - 'uuml': 0x00fc, # latin small letter u with diaeresis, U+00FC ISOlat1 - 'weierp': 0x2118, # script capital P = power set = Weierstrass p, U+2118 ISOamso - 'xi': 0x03be, # greek small letter xi, U+03BE ISOgrk3 - 'yacute': 0x00fd, # latin small letter y with acute, U+00FD ISOlat1 - 'yen': 0x00a5, # yen sign = yuan sign, U+00A5 ISOnum - 'yuml': 0x00ff, # latin small letter y with diaeresis, U+00FF ISOlat1 - 'zeta': 0x03b6, # greek small letter zeta, U+03B6 ISOgrk3 - 'zwj': 0x200d, # zero width joiner, U+200D NEW RFC 2070 - 'zwnj': 0x200c, # zero width non-joiner, U+200C NEW RFC 2070 -} - - -# maps the HTML5 named character references to the equivalent Unicode character(s) -html5 = { - 'Aacute': '\xc1', - 'aacute': '\xe1', - 'Aacute;': '\xc1', - 'aacute;': '\xe1', - 'Abreve;': '\u0102', - 'abreve;': '\u0103', - 'ac;': '\u223e', - 'acd;': '\u223f', - 'acE;': '\u223e\u0333', - 'Acirc': '\xc2', - 'acirc': '\xe2', - 'Acirc;': '\xc2', - 'acirc;': '\xe2', - 'acute': '\xb4', - 'acute;': '\xb4', - 'Acy;': '\u0410', - 'acy;': '\u0430', - 'AElig': '\xc6', - 'aelig': '\xe6', - 'AElig;': '\xc6', - 'aelig;': '\xe6', - 'af;': '\u2061', - 'Afr;': '\U0001d504', - 'afr;': '\U0001d51e', - 'Agrave': '\xc0', - 'agrave': '\xe0', - 'Agrave;': '\xc0', - 'agrave;': '\xe0', - 'alefsym;': '\u2135', - 'aleph;': '\u2135', - 'Alpha;': '\u0391', - 'alpha;': '\u03b1', - 'Amacr;': '\u0100', - 'amacr;': '\u0101', - 'amalg;': '\u2a3f', - 'AMP': '&', - 'amp': '&', - 'AMP;': '&', - 'amp;': '&', - 'And;': '\u2a53', - 'and;': '\u2227', - 'andand;': '\u2a55', - 'andd;': '\u2a5c', - 'andslope;': '\u2a58', - 'andv;': '\u2a5a', - 'ang;': '\u2220', - 'ange;': '\u29a4', - 'angle;': '\u2220', - 'angmsd;': '\u2221', - 'angmsdaa;': '\u29a8', - 'angmsdab;': '\u29a9', - 'angmsdac;': '\u29aa', - 'angmsdad;': '\u29ab', - 'angmsdae;': '\u29ac', - 'angmsdaf;': '\u29ad', - 'angmsdag;': '\u29ae', - 'angmsdah;': '\u29af', - 'angrt;': '\u221f', - 'angrtvb;': '\u22be', - 'angrtvbd;': '\u299d', - 'angsph;': '\u2222', - 'angst;': '\xc5', - 'angzarr;': '\u237c', - 'Aogon;': '\u0104', - 'aogon;': '\u0105', - 'Aopf;': '\U0001d538', - 'aopf;': '\U0001d552', - 'ap;': '\u2248', - 'apacir;': '\u2a6f', - 'apE;': '\u2a70', - 'ape;': '\u224a', - 'apid;': '\u224b', - 'apos;': "'", - 'ApplyFunction;': '\u2061', - 'approx;': '\u2248', - 'approxeq;': '\u224a', - 'Aring': '\xc5', - 'aring': '\xe5', - 'Aring;': '\xc5', - 'aring;': '\xe5', - 'Ascr;': '\U0001d49c', - 'ascr;': '\U0001d4b6', - 'Assign;': '\u2254', - 'ast;': '*', - 'asymp;': '\u2248', - 'asympeq;': '\u224d', - 'Atilde': '\xc3', - 'atilde': '\xe3', - 'Atilde;': '\xc3', - 'atilde;': '\xe3', - 'Auml': '\xc4', - 'auml': '\xe4', - 'Auml;': '\xc4', - 'auml;': '\xe4', - 'awconint;': '\u2233', - 'awint;': '\u2a11', - 'backcong;': '\u224c', - 'backepsilon;': '\u03f6', - 'backprime;': '\u2035', - 'backsim;': '\u223d', - 'backsimeq;': '\u22cd', - 'Backslash;': '\u2216', - 'Barv;': '\u2ae7', - 'barvee;': '\u22bd', - 'Barwed;': '\u2306', - 'barwed;': '\u2305', - 'barwedge;': '\u2305', - 'bbrk;': '\u23b5', - 'bbrktbrk;': '\u23b6', - 'bcong;': '\u224c', - 'Bcy;': '\u0411', - 'bcy;': '\u0431', - 'bdquo;': '\u201e', - 'becaus;': '\u2235', - 'Because;': '\u2235', - 'because;': '\u2235', - 'bemptyv;': '\u29b0', - 'bepsi;': '\u03f6', - 'bernou;': '\u212c', - 'Bernoullis;': '\u212c', - 'Beta;': '\u0392', - 'beta;': '\u03b2', - 'beth;': '\u2136', - 'between;': '\u226c', - 'Bfr;': '\U0001d505', - 'bfr;': '\U0001d51f', - 'bigcap;': '\u22c2', - 'bigcirc;': '\u25ef', - 'bigcup;': '\u22c3', - 'bigodot;': '\u2a00', - 'bigoplus;': '\u2a01', - 'bigotimes;': '\u2a02', - 'bigsqcup;': '\u2a06', - 'bigstar;': '\u2605', - 'bigtriangledown;': '\u25bd', - 'bigtriangleup;': '\u25b3', - 'biguplus;': '\u2a04', - 'bigvee;': '\u22c1', - 'bigwedge;': '\u22c0', - 'bkarow;': '\u290d', - 'blacklozenge;': '\u29eb', - 'blacksquare;': '\u25aa', - 'blacktriangle;': '\u25b4', - 'blacktriangledown;': '\u25be', - 'blacktriangleleft;': '\u25c2', - 'blacktriangleright;': '\u25b8', - 'blank;': '\u2423', - 'blk12;': '\u2592', - 'blk14;': '\u2591', - 'blk34;': '\u2593', - 'block;': '\u2588', - 'bne;': '=\u20e5', - 'bnequiv;': '\u2261\u20e5', - 'bNot;': '\u2aed', - 'bnot;': '\u2310', - 'Bopf;': '\U0001d539', - 'bopf;': '\U0001d553', - 'bot;': '\u22a5', - 'bottom;': '\u22a5', - 'bowtie;': '\u22c8', - 'boxbox;': '\u29c9', - 'boxDL;': '\u2557', - 'boxDl;': '\u2556', - 'boxdL;': '\u2555', - 'boxdl;': '\u2510', - 'boxDR;': '\u2554', - 'boxDr;': '\u2553', - 'boxdR;': '\u2552', - 'boxdr;': '\u250c', - 'boxH;': '\u2550', - 'boxh;': '\u2500', - 'boxHD;': '\u2566', - 'boxHd;': '\u2564', - 'boxhD;': '\u2565', - 'boxhd;': '\u252c', - 'boxHU;': '\u2569', - 'boxHu;': '\u2567', - 'boxhU;': '\u2568', - 'boxhu;': '\u2534', - 'boxminus;': '\u229f', - 'boxplus;': '\u229e', - 'boxtimes;': '\u22a0', - 'boxUL;': '\u255d', - 'boxUl;': '\u255c', - 'boxuL;': '\u255b', - 'boxul;': '\u2518', - 'boxUR;': '\u255a', - 'boxUr;': '\u2559', - 'boxuR;': '\u2558', - 'boxur;': '\u2514', - 'boxV;': '\u2551', - 'boxv;': '\u2502', - 'boxVH;': '\u256c', - 'boxVh;': '\u256b', - 'boxvH;': '\u256a', - 'boxvh;': '\u253c', - 'boxVL;': '\u2563', - 'boxVl;': '\u2562', - 'boxvL;': '\u2561', - 'boxvl;': '\u2524', - 'boxVR;': '\u2560', - 'boxVr;': '\u255f', - 'boxvR;': '\u255e', - 'boxvr;': '\u251c', - 'bprime;': '\u2035', - 'Breve;': '\u02d8', - 'breve;': '\u02d8', - 'brvbar': '\xa6', - 'brvbar;': '\xa6', - 'Bscr;': '\u212c', - 'bscr;': '\U0001d4b7', - 'bsemi;': '\u204f', - 'bsim;': '\u223d', - 'bsime;': '\u22cd', - 'bsol;': '\\', - 'bsolb;': '\u29c5', - 'bsolhsub;': '\u27c8', - 'bull;': '\u2022', - 'bullet;': '\u2022', - 'bump;': '\u224e', - 'bumpE;': '\u2aae', - 'bumpe;': '\u224f', - 'Bumpeq;': '\u224e', - 'bumpeq;': '\u224f', - 'Cacute;': '\u0106', - 'cacute;': '\u0107', - 'Cap;': '\u22d2', - 'cap;': '\u2229', - 'capand;': '\u2a44', - 'capbrcup;': '\u2a49', - 'capcap;': '\u2a4b', - 'capcup;': '\u2a47', - 'capdot;': '\u2a40', - 'CapitalDifferentialD;': '\u2145', - 'caps;': '\u2229\ufe00', - 'caret;': '\u2041', - 'caron;': '\u02c7', - 'Cayleys;': '\u212d', - 'ccaps;': '\u2a4d', - 'Ccaron;': '\u010c', - 'ccaron;': '\u010d', - 'Ccedil': '\xc7', - 'ccedil': '\xe7', - 'Ccedil;': '\xc7', - 'ccedil;': '\xe7', - 'Ccirc;': '\u0108', - 'ccirc;': '\u0109', - 'Cconint;': '\u2230', - 'ccups;': '\u2a4c', - 'ccupssm;': '\u2a50', - 'Cdot;': '\u010a', - 'cdot;': '\u010b', - 'cedil': '\xb8', - 'cedil;': '\xb8', - 'Cedilla;': '\xb8', - 'cemptyv;': '\u29b2', - 'cent': '\xa2', - 'cent;': '\xa2', - 'CenterDot;': '\xb7', - 'centerdot;': '\xb7', - 'Cfr;': '\u212d', - 'cfr;': '\U0001d520', - 'CHcy;': '\u0427', - 'chcy;': '\u0447', - 'check;': '\u2713', - 'checkmark;': '\u2713', - 'Chi;': '\u03a7', - 'chi;': '\u03c7', - 'cir;': '\u25cb', - 'circ;': '\u02c6', - 'circeq;': '\u2257', - 'circlearrowleft;': '\u21ba', - 'circlearrowright;': '\u21bb', - 'circledast;': '\u229b', - 'circledcirc;': '\u229a', - 'circleddash;': '\u229d', - 'CircleDot;': '\u2299', - 'circledR;': '\xae', - 'circledS;': '\u24c8', - 'CircleMinus;': '\u2296', - 'CirclePlus;': '\u2295', - 'CircleTimes;': '\u2297', - 'cirE;': '\u29c3', - 'cire;': '\u2257', - 'cirfnint;': '\u2a10', - 'cirmid;': '\u2aef', - 'cirscir;': '\u29c2', - 'ClockwiseContourIntegral;': '\u2232', - 'CloseCurlyDoubleQuote;': '\u201d', - 'CloseCurlyQuote;': '\u2019', - 'clubs;': '\u2663', - 'clubsuit;': '\u2663', - 'Colon;': '\u2237', - 'colon;': ':', - 'Colone;': '\u2a74', - 'colone;': '\u2254', - 'coloneq;': '\u2254', - 'comma;': ',', - 'commat;': '@', - 'comp;': '\u2201', - 'compfn;': '\u2218', - 'complement;': '\u2201', - 'complexes;': '\u2102', - 'cong;': '\u2245', - 'congdot;': '\u2a6d', - 'Congruent;': '\u2261', - 'Conint;': '\u222f', - 'conint;': '\u222e', - 'ContourIntegral;': '\u222e', - 'Copf;': '\u2102', - 'copf;': '\U0001d554', - 'coprod;': '\u2210', - 'Coproduct;': '\u2210', - 'COPY': '\xa9', - 'copy': '\xa9', - 'COPY;': '\xa9', - 'copy;': '\xa9', - 'copysr;': '\u2117', - 'CounterClockwiseContourIntegral;': '\u2233', - 'crarr;': '\u21b5', - 'Cross;': '\u2a2f', - 'cross;': '\u2717', - 'Cscr;': '\U0001d49e', - 'cscr;': '\U0001d4b8', - 'csub;': '\u2acf', - 'csube;': '\u2ad1', - 'csup;': '\u2ad0', - 'csupe;': '\u2ad2', - 'ctdot;': '\u22ef', - 'cudarrl;': '\u2938', - 'cudarrr;': '\u2935', - 'cuepr;': '\u22de', - 'cuesc;': '\u22df', - 'cularr;': '\u21b6', - 'cularrp;': '\u293d', - 'Cup;': '\u22d3', - 'cup;': '\u222a', - 'cupbrcap;': '\u2a48', - 'CupCap;': '\u224d', - 'cupcap;': '\u2a46', - 'cupcup;': '\u2a4a', - 'cupdot;': '\u228d', - 'cupor;': '\u2a45', - 'cups;': '\u222a\ufe00', - 'curarr;': '\u21b7', - 'curarrm;': '\u293c', - 'curlyeqprec;': '\u22de', - 'curlyeqsucc;': '\u22df', - 'curlyvee;': '\u22ce', - 'curlywedge;': '\u22cf', - 'curren': '\xa4', - 'curren;': '\xa4', - 'curvearrowleft;': '\u21b6', - 'curvearrowright;': '\u21b7', - 'cuvee;': '\u22ce', - 'cuwed;': '\u22cf', - 'cwconint;': '\u2232', - 'cwint;': '\u2231', - 'cylcty;': '\u232d', - 'Dagger;': '\u2021', - 'dagger;': '\u2020', - 'daleth;': '\u2138', - 'Darr;': '\u21a1', - 'dArr;': '\u21d3', - 'darr;': '\u2193', - 'dash;': '\u2010', - 'Dashv;': '\u2ae4', - 'dashv;': '\u22a3', - 'dbkarow;': '\u290f', - 'dblac;': '\u02dd', - 'Dcaron;': '\u010e', - 'dcaron;': '\u010f', - 'Dcy;': '\u0414', - 'dcy;': '\u0434', - 'DD;': '\u2145', - 'dd;': '\u2146', - 'ddagger;': '\u2021', - 'ddarr;': '\u21ca', - 'DDotrahd;': '\u2911', - 'ddotseq;': '\u2a77', - 'deg': '\xb0', - 'deg;': '\xb0', - 'Del;': '\u2207', - 'Delta;': '\u0394', - 'delta;': '\u03b4', - 'demptyv;': '\u29b1', - 'dfisht;': '\u297f', - 'Dfr;': '\U0001d507', - 'dfr;': '\U0001d521', - 'dHar;': '\u2965', - 'dharl;': '\u21c3', - 'dharr;': '\u21c2', - 'DiacriticalAcute;': '\xb4', - 'DiacriticalDot;': '\u02d9', - 'DiacriticalDoubleAcute;': '\u02dd', - 'DiacriticalGrave;': '`', - 'DiacriticalTilde;': '\u02dc', - 'diam;': '\u22c4', - 'Diamond;': '\u22c4', - 'diamond;': '\u22c4', - 'diamondsuit;': '\u2666', - 'diams;': '\u2666', - 'die;': '\xa8', - 'DifferentialD;': '\u2146', - 'digamma;': '\u03dd', - 'disin;': '\u22f2', - 'div;': '\xf7', - 'divide': '\xf7', - 'divide;': '\xf7', - 'divideontimes;': '\u22c7', - 'divonx;': '\u22c7', - 'DJcy;': '\u0402', - 'djcy;': '\u0452', - 'dlcorn;': '\u231e', - 'dlcrop;': '\u230d', - 'dollar;': '$', - 'Dopf;': '\U0001d53b', - 'dopf;': '\U0001d555', - 'Dot;': '\xa8', - 'dot;': '\u02d9', - 'DotDot;': '\u20dc', - 'doteq;': '\u2250', - 'doteqdot;': '\u2251', - 'DotEqual;': '\u2250', - 'dotminus;': '\u2238', - 'dotplus;': '\u2214', - 'dotsquare;': '\u22a1', - 'doublebarwedge;': '\u2306', - 'DoubleContourIntegral;': '\u222f', - 'DoubleDot;': '\xa8', - 'DoubleDownArrow;': '\u21d3', - 'DoubleLeftArrow;': '\u21d0', - 'DoubleLeftRightArrow;': '\u21d4', - 'DoubleLeftTee;': '\u2ae4', - 'DoubleLongLeftArrow;': '\u27f8', - 'DoubleLongLeftRightArrow;': '\u27fa', - 'DoubleLongRightArrow;': '\u27f9', - 'DoubleRightArrow;': '\u21d2', - 'DoubleRightTee;': '\u22a8', - 'DoubleUpArrow;': '\u21d1', - 'DoubleUpDownArrow;': '\u21d5', - 'DoubleVerticalBar;': '\u2225', - 'DownArrow;': '\u2193', - 'Downarrow;': '\u21d3', - 'downarrow;': '\u2193', - 'DownArrowBar;': '\u2913', - 'DownArrowUpArrow;': '\u21f5', - 'DownBreve;': '\u0311', - 'downdownarrows;': '\u21ca', - 'downharpoonleft;': '\u21c3', - 'downharpoonright;': '\u21c2', - 'DownLeftRightVector;': '\u2950', - 'DownLeftTeeVector;': '\u295e', - 'DownLeftVector;': '\u21bd', - 'DownLeftVectorBar;': '\u2956', - 'DownRightTeeVector;': '\u295f', - 'DownRightVector;': '\u21c1', - 'DownRightVectorBar;': '\u2957', - 'DownTee;': '\u22a4', - 'DownTeeArrow;': '\u21a7', - 'drbkarow;': '\u2910', - 'drcorn;': '\u231f', - 'drcrop;': '\u230c', - 'Dscr;': '\U0001d49f', - 'dscr;': '\U0001d4b9', - 'DScy;': '\u0405', - 'dscy;': '\u0455', - 'dsol;': '\u29f6', - 'Dstrok;': '\u0110', - 'dstrok;': '\u0111', - 'dtdot;': '\u22f1', - 'dtri;': '\u25bf', - 'dtrif;': '\u25be', - 'duarr;': '\u21f5', - 'duhar;': '\u296f', - 'dwangle;': '\u29a6', - 'DZcy;': '\u040f', - 'dzcy;': '\u045f', - 'dzigrarr;': '\u27ff', - 'Eacute': '\xc9', - 'eacute': '\xe9', - 'Eacute;': '\xc9', - 'eacute;': '\xe9', - 'easter;': '\u2a6e', - 'Ecaron;': '\u011a', - 'ecaron;': '\u011b', - 'ecir;': '\u2256', - 'Ecirc': '\xca', - 'ecirc': '\xea', - 'Ecirc;': '\xca', - 'ecirc;': '\xea', - 'ecolon;': '\u2255', - 'Ecy;': '\u042d', - 'ecy;': '\u044d', - 'eDDot;': '\u2a77', - 'Edot;': '\u0116', - 'eDot;': '\u2251', - 'edot;': '\u0117', - 'ee;': '\u2147', - 'efDot;': '\u2252', - 'Efr;': '\U0001d508', - 'efr;': '\U0001d522', - 'eg;': '\u2a9a', - 'Egrave': '\xc8', - 'egrave': '\xe8', - 'Egrave;': '\xc8', - 'egrave;': '\xe8', - 'egs;': '\u2a96', - 'egsdot;': '\u2a98', - 'el;': '\u2a99', - 'Element;': '\u2208', - 'elinters;': '\u23e7', - 'ell;': '\u2113', - 'els;': '\u2a95', - 'elsdot;': '\u2a97', - 'Emacr;': '\u0112', - 'emacr;': '\u0113', - 'empty;': '\u2205', - 'emptyset;': '\u2205', - 'EmptySmallSquare;': '\u25fb', - 'emptyv;': '\u2205', - 'EmptyVerySmallSquare;': '\u25ab', - 'emsp13;': '\u2004', - 'emsp14;': '\u2005', - 'emsp;': '\u2003', - 'ENG;': '\u014a', - 'eng;': '\u014b', - 'ensp;': '\u2002', - 'Eogon;': '\u0118', - 'eogon;': '\u0119', - 'Eopf;': '\U0001d53c', - 'eopf;': '\U0001d556', - 'epar;': '\u22d5', - 'eparsl;': '\u29e3', - 'eplus;': '\u2a71', - 'epsi;': '\u03b5', - 'Epsilon;': '\u0395', - 'epsilon;': '\u03b5', - 'epsiv;': '\u03f5', - 'eqcirc;': '\u2256', - 'eqcolon;': '\u2255', - 'eqsim;': '\u2242', - 'eqslantgtr;': '\u2a96', - 'eqslantless;': '\u2a95', - 'Equal;': '\u2a75', - 'equals;': '=', - 'EqualTilde;': '\u2242', - 'equest;': '\u225f', - 'Equilibrium;': '\u21cc', - 'equiv;': '\u2261', - 'equivDD;': '\u2a78', - 'eqvparsl;': '\u29e5', - 'erarr;': '\u2971', - 'erDot;': '\u2253', - 'Escr;': '\u2130', - 'escr;': '\u212f', - 'esdot;': '\u2250', - 'Esim;': '\u2a73', - 'esim;': '\u2242', - 'Eta;': '\u0397', - 'eta;': '\u03b7', - 'ETH': '\xd0', - 'eth': '\xf0', - 'ETH;': '\xd0', - 'eth;': '\xf0', - 'Euml': '\xcb', - 'euml': '\xeb', - 'Euml;': '\xcb', - 'euml;': '\xeb', - 'euro;': '\u20ac', - 'excl;': '!', - 'exist;': '\u2203', - 'Exists;': '\u2203', - 'expectation;': '\u2130', - 'ExponentialE;': '\u2147', - 'exponentiale;': '\u2147', - 'fallingdotseq;': '\u2252', - 'Fcy;': '\u0424', - 'fcy;': '\u0444', - 'female;': '\u2640', - 'ffilig;': '\ufb03', - 'fflig;': '\ufb00', - 'ffllig;': '\ufb04', - 'Ffr;': '\U0001d509', - 'ffr;': '\U0001d523', - 'filig;': '\ufb01', - 'FilledSmallSquare;': '\u25fc', - 'FilledVerySmallSquare;': '\u25aa', - 'fjlig;': 'fj', - 'flat;': '\u266d', - 'fllig;': '\ufb02', - 'fltns;': '\u25b1', - 'fnof;': '\u0192', - 'Fopf;': '\U0001d53d', - 'fopf;': '\U0001d557', - 'ForAll;': '\u2200', - 'forall;': '\u2200', - 'fork;': '\u22d4', - 'forkv;': '\u2ad9', - 'Fouriertrf;': '\u2131', - 'fpartint;': '\u2a0d', - 'frac12': '\xbd', - 'frac12;': '\xbd', - 'frac13;': '\u2153', - 'frac14': '\xbc', - 'frac14;': '\xbc', - 'frac15;': '\u2155', - 'frac16;': '\u2159', - 'frac18;': '\u215b', - 'frac23;': '\u2154', - 'frac25;': '\u2156', - 'frac34': '\xbe', - 'frac34;': '\xbe', - 'frac35;': '\u2157', - 'frac38;': '\u215c', - 'frac45;': '\u2158', - 'frac56;': '\u215a', - 'frac58;': '\u215d', - 'frac78;': '\u215e', - 'frasl;': '\u2044', - 'frown;': '\u2322', - 'Fscr;': '\u2131', - 'fscr;': '\U0001d4bb', - 'gacute;': '\u01f5', - 'Gamma;': '\u0393', - 'gamma;': '\u03b3', - 'Gammad;': '\u03dc', - 'gammad;': '\u03dd', - 'gap;': '\u2a86', - 'Gbreve;': '\u011e', - 'gbreve;': '\u011f', - 'Gcedil;': '\u0122', - 'Gcirc;': '\u011c', - 'gcirc;': '\u011d', - 'Gcy;': '\u0413', - 'gcy;': '\u0433', - 'Gdot;': '\u0120', - 'gdot;': '\u0121', - 'gE;': '\u2267', - 'ge;': '\u2265', - 'gEl;': '\u2a8c', - 'gel;': '\u22db', - 'geq;': '\u2265', - 'geqq;': '\u2267', - 'geqslant;': '\u2a7e', - 'ges;': '\u2a7e', - 'gescc;': '\u2aa9', - 'gesdot;': '\u2a80', - 'gesdoto;': '\u2a82', - 'gesdotol;': '\u2a84', - 'gesl;': '\u22db\ufe00', - 'gesles;': '\u2a94', - 'Gfr;': '\U0001d50a', - 'gfr;': '\U0001d524', - 'Gg;': '\u22d9', - 'gg;': '\u226b', - 'ggg;': '\u22d9', - 'gimel;': '\u2137', - 'GJcy;': '\u0403', - 'gjcy;': '\u0453', - 'gl;': '\u2277', - 'gla;': '\u2aa5', - 'glE;': '\u2a92', - 'glj;': '\u2aa4', - 'gnap;': '\u2a8a', - 'gnapprox;': '\u2a8a', - 'gnE;': '\u2269', - 'gne;': '\u2a88', - 'gneq;': '\u2a88', - 'gneqq;': '\u2269', - 'gnsim;': '\u22e7', - 'Gopf;': '\U0001d53e', - 'gopf;': '\U0001d558', - 'grave;': '`', - 'GreaterEqual;': '\u2265', - 'GreaterEqualLess;': '\u22db', - 'GreaterFullEqual;': '\u2267', - 'GreaterGreater;': '\u2aa2', - 'GreaterLess;': '\u2277', - 'GreaterSlantEqual;': '\u2a7e', - 'GreaterTilde;': '\u2273', - 'Gscr;': '\U0001d4a2', - 'gscr;': '\u210a', - 'gsim;': '\u2273', - 'gsime;': '\u2a8e', - 'gsiml;': '\u2a90', - 'GT': '>', - 'gt': '>', - 'GT;': '>', - 'Gt;': '\u226b', - 'gt;': '>', - 'gtcc;': '\u2aa7', - 'gtcir;': '\u2a7a', - 'gtdot;': '\u22d7', - 'gtlPar;': '\u2995', - 'gtquest;': '\u2a7c', - 'gtrapprox;': '\u2a86', - 'gtrarr;': '\u2978', - 'gtrdot;': '\u22d7', - 'gtreqless;': '\u22db', - 'gtreqqless;': '\u2a8c', - 'gtrless;': '\u2277', - 'gtrsim;': '\u2273', - 'gvertneqq;': '\u2269\ufe00', - 'gvnE;': '\u2269\ufe00', - 'Hacek;': '\u02c7', - 'hairsp;': '\u200a', - 'half;': '\xbd', - 'hamilt;': '\u210b', - 'HARDcy;': '\u042a', - 'hardcy;': '\u044a', - 'hArr;': '\u21d4', - 'harr;': '\u2194', - 'harrcir;': '\u2948', - 'harrw;': '\u21ad', - 'Hat;': '^', - 'hbar;': '\u210f', - 'Hcirc;': '\u0124', - 'hcirc;': '\u0125', - 'hearts;': '\u2665', - 'heartsuit;': '\u2665', - 'hellip;': '\u2026', - 'hercon;': '\u22b9', - 'Hfr;': '\u210c', - 'hfr;': '\U0001d525', - 'HilbertSpace;': '\u210b', - 'hksearow;': '\u2925', - 'hkswarow;': '\u2926', - 'hoarr;': '\u21ff', - 'homtht;': '\u223b', - 'hookleftarrow;': '\u21a9', - 'hookrightarrow;': '\u21aa', - 'Hopf;': '\u210d', - 'hopf;': '\U0001d559', - 'horbar;': '\u2015', - 'HorizontalLine;': '\u2500', - 'Hscr;': '\u210b', - 'hscr;': '\U0001d4bd', - 'hslash;': '\u210f', - 'Hstrok;': '\u0126', - 'hstrok;': '\u0127', - 'HumpDownHump;': '\u224e', - 'HumpEqual;': '\u224f', - 'hybull;': '\u2043', - 'hyphen;': '\u2010', - 'Iacute': '\xcd', - 'iacute': '\xed', - 'Iacute;': '\xcd', - 'iacute;': '\xed', - 'ic;': '\u2063', - 'Icirc': '\xce', - 'icirc': '\xee', - 'Icirc;': '\xce', - 'icirc;': '\xee', - 'Icy;': '\u0418', - 'icy;': '\u0438', - 'Idot;': '\u0130', - 'IEcy;': '\u0415', - 'iecy;': '\u0435', - 'iexcl': '\xa1', - 'iexcl;': '\xa1', - 'iff;': '\u21d4', - 'Ifr;': '\u2111', - 'ifr;': '\U0001d526', - 'Igrave': '\xcc', - 'igrave': '\xec', - 'Igrave;': '\xcc', - 'igrave;': '\xec', - 'ii;': '\u2148', - 'iiiint;': '\u2a0c', - 'iiint;': '\u222d', - 'iinfin;': '\u29dc', - 'iiota;': '\u2129', - 'IJlig;': '\u0132', - 'ijlig;': '\u0133', - 'Im;': '\u2111', - 'Imacr;': '\u012a', - 'imacr;': '\u012b', - 'image;': '\u2111', - 'ImaginaryI;': '\u2148', - 'imagline;': '\u2110', - 'imagpart;': '\u2111', - 'imath;': '\u0131', - 'imof;': '\u22b7', - 'imped;': '\u01b5', - 'Implies;': '\u21d2', - 'in;': '\u2208', - 'incare;': '\u2105', - 'infin;': '\u221e', - 'infintie;': '\u29dd', - 'inodot;': '\u0131', - 'Int;': '\u222c', - 'int;': '\u222b', - 'intcal;': '\u22ba', - 'integers;': '\u2124', - 'Integral;': '\u222b', - 'intercal;': '\u22ba', - 'Intersection;': '\u22c2', - 'intlarhk;': '\u2a17', - 'intprod;': '\u2a3c', - 'InvisibleComma;': '\u2063', - 'InvisibleTimes;': '\u2062', - 'IOcy;': '\u0401', - 'iocy;': '\u0451', - 'Iogon;': '\u012e', - 'iogon;': '\u012f', - 'Iopf;': '\U0001d540', - 'iopf;': '\U0001d55a', - 'Iota;': '\u0399', - 'iota;': '\u03b9', - 'iprod;': '\u2a3c', - 'iquest': '\xbf', - 'iquest;': '\xbf', - 'Iscr;': '\u2110', - 'iscr;': '\U0001d4be', - 'isin;': '\u2208', - 'isindot;': '\u22f5', - 'isinE;': '\u22f9', - 'isins;': '\u22f4', - 'isinsv;': '\u22f3', - 'isinv;': '\u2208', - 'it;': '\u2062', - 'Itilde;': '\u0128', - 'itilde;': '\u0129', - 'Iukcy;': '\u0406', - 'iukcy;': '\u0456', - 'Iuml': '\xcf', - 'iuml': '\xef', - 'Iuml;': '\xcf', - 'iuml;': '\xef', - 'Jcirc;': '\u0134', - 'jcirc;': '\u0135', - 'Jcy;': '\u0419', - 'jcy;': '\u0439', - 'Jfr;': '\U0001d50d', - 'jfr;': '\U0001d527', - 'jmath;': '\u0237', - 'Jopf;': '\U0001d541', - 'jopf;': '\U0001d55b', - 'Jscr;': '\U0001d4a5', - 'jscr;': '\U0001d4bf', - 'Jsercy;': '\u0408', - 'jsercy;': '\u0458', - 'Jukcy;': '\u0404', - 'jukcy;': '\u0454', - 'Kappa;': '\u039a', - 'kappa;': '\u03ba', - 'kappav;': '\u03f0', - 'Kcedil;': '\u0136', - 'kcedil;': '\u0137', - 'Kcy;': '\u041a', - 'kcy;': '\u043a', - 'Kfr;': '\U0001d50e', - 'kfr;': '\U0001d528', - 'kgreen;': '\u0138', - 'KHcy;': '\u0425', - 'khcy;': '\u0445', - 'KJcy;': '\u040c', - 'kjcy;': '\u045c', - 'Kopf;': '\U0001d542', - 'kopf;': '\U0001d55c', - 'Kscr;': '\U0001d4a6', - 'kscr;': '\U0001d4c0', - 'lAarr;': '\u21da', - 'Lacute;': '\u0139', - 'lacute;': '\u013a', - 'laemptyv;': '\u29b4', - 'lagran;': '\u2112', - 'Lambda;': '\u039b', - 'lambda;': '\u03bb', - 'Lang;': '\u27ea', - 'lang;': '\u27e8', - 'langd;': '\u2991', - 'langle;': '\u27e8', - 'lap;': '\u2a85', - 'Laplacetrf;': '\u2112', - 'laquo': '\xab', - 'laquo;': '\xab', - 'Larr;': '\u219e', - 'lArr;': '\u21d0', - 'larr;': '\u2190', - 'larrb;': '\u21e4', - 'larrbfs;': '\u291f', - 'larrfs;': '\u291d', - 'larrhk;': '\u21a9', - 'larrlp;': '\u21ab', - 'larrpl;': '\u2939', - 'larrsim;': '\u2973', - 'larrtl;': '\u21a2', - 'lat;': '\u2aab', - 'lAtail;': '\u291b', - 'latail;': '\u2919', - 'late;': '\u2aad', - 'lates;': '\u2aad\ufe00', - 'lBarr;': '\u290e', - 'lbarr;': '\u290c', - 'lbbrk;': '\u2772', - 'lbrace;': '{', - 'lbrack;': '[', - 'lbrke;': '\u298b', - 'lbrksld;': '\u298f', - 'lbrkslu;': '\u298d', - 'Lcaron;': '\u013d', - 'lcaron;': '\u013e', - 'Lcedil;': '\u013b', - 'lcedil;': '\u013c', - 'lceil;': '\u2308', - 'lcub;': '{', - 'Lcy;': '\u041b', - 'lcy;': '\u043b', - 'ldca;': '\u2936', - 'ldquo;': '\u201c', - 'ldquor;': '\u201e', - 'ldrdhar;': '\u2967', - 'ldrushar;': '\u294b', - 'ldsh;': '\u21b2', - 'lE;': '\u2266', - 'le;': '\u2264', - 'LeftAngleBracket;': '\u27e8', - 'LeftArrow;': '\u2190', - 'Leftarrow;': '\u21d0', - 'leftarrow;': '\u2190', - 'LeftArrowBar;': '\u21e4', - 'LeftArrowRightArrow;': '\u21c6', - 'leftarrowtail;': '\u21a2', - 'LeftCeiling;': '\u2308', - 'LeftDoubleBracket;': '\u27e6', - 'LeftDownTeeVector;': '\u2961', - 'LeftDownVector;': '\u21c3', - 'LeftDownVectorBar;': '\u2959', - 'LeftFloor;': '\u230a', - 'leftharpoondown;': '\u21bd', - 'leftharpoonup;': '\u21bc', - 'leftleftarrows;': '\u21c7', - 'LeftRightArrow;': '\u2194', - 'Leftrightarrow;': '\u21d4', - 'leftrightarrow;': '\u2194', - 'leftrightarrows;': '\u21c6', - 'leftrightharpoons;': '\u21cb', - 'leftrightsquigarrow;': '\u21ad', - 'LeftRightVector;': '\u294e', - 'LeftTee;': '\u22a3', - 'LeftTeeArrow;': '\u21a4', - 'LeftTeeVector;': '\u295a', - 'leftthreetimes;': '\u22cb', - 'LeftTriangle;': '\u22b2', - 'LeftTriangleBar;': '\u29cf', - 'LeftTriangleEqual;': '\u22b4', - 'LeftUpDownVector;': '\u2951', - 'LeftUpTeeVector;': '\u2960', - 'LeftUpVector;': '\u21bf', - 'LeftUpVectorBar;': '\u2958', - 'LeftVector;': '\u21bc', - 'LeftVectorBar;': '\u2952', - 'lEg;': '\u2a8b', - 'leg;': '\u22da', - 'leq;': '\u2264', - 'leqq;': '\u2266', - 'leqslant;': '\u2a7d', - 'les;': '\u2a7d', - 'lescc;': '\u2aa8', - 'lesdot;': '\u2a7f', - 'lesdoto;': '\u2a81', - 'lesdotor;': '\u2a83', - 'lesg;': '\u22da\ufe00', - 'lesges;': '\u2a93', - 'lessapprox;': '\u2a85', - 'lessdot;': '\u22d6', - 'lesseqgtr;': '\u22da', - 'lesseqqgtr;': '\u2a8b', - 'LessEqualGreater;': '\u22da', - 'LessFullEqual;': '\u2266', - 'LessGreater;': '\u2276', - 'lessgtr;': '\u2276', - 'LessLess;': '\u2aa1', - 'lesssim;': '\u2272', - 'LessSlantEqual;': '\u2a7d', - 'LessTilde;': '\u2272', - 'lfisht;': '\u297c', - 'lfloor;': '\u230a', - 'Lfr;': '\U0001d50f', - 'lfr;': '\U0001d529', - 'lg;': '\u2276', - 'lgE;': '\u2a91', - 'lHar;': '\u2962', - 'lhard;': '\u21bd', - 'lharu;': '\u21bc', - 'lharul;': '\u296a', - 'lhblk;': '\u2584', - 'LJcy;': '\u0409', - 'ljcy;': '\u0459', - 'Ll;': '\u22d8', - 'll;': '\u226a', - 'llarr;': '\u21c7', - 'llcorner;': '\u231e', - 'Lleftarrow;': '\u21da', - 'llhard;': '\u296b', - 'lltri;': '\u25fa', - 'Lmidot;': '\u013f', - 'lmidot;': '\u0140', - 'lmoust;': '\u23b0', - 'lmoustache;': '\u23b0', - 'lnap;': '\u2a89', - 'lnapprox;': '\u2a89', - 'lnE;': '\u2268', - 'lne;': '\u2a87', - 'lneq;': '\u2a87', - 'lneqq;': '\u2268', - 'lnsim;': '\u22e6', - 'loang;': '\u27ec', - 'loarr;': '\u21fd', - 'lobrk;': '\u27e6', - 'LongLeftArrow;': '\u27f5', - 'Longleftarrow;': '\u27f8', - 'longleftarrow;': '\u27f5', - 'LongLeftRightArrow;': '\u27f7', - 'Longleftrightarrow;': '\u27fa', - 'longleftrightarrow;': '\u27f7', - 'longmapsto;': '\u27fc', - 'LongRightArrow;': '\u27f6', - 'Longrightarrow;': '\u27f9', - 'longrightarrow;': '\u27f6', - 'looparrowleft;': '\u21ab', - 'looparrowright;': '\u21ac', - 'lopar;': '\u2985', - 'Lopf;': '\U0001d543', - 'lopf;': '\U0001d55d', - 'loplus;': '\u2a2d', - 'lotimes;': '\u2a34', - 'lowast;': '\u2217', - 'lowbar;': '_', - 'LowerLeftArrow;': '\u2199', - 'LowerRightArrow;': '\u2198', - 'loz;': '\u25ca', - 'lozenge;': '\u25ca', - 'lozf;': '\u29eb', - 'lpar;': '(', - 'lparlt;': '\u2993', - 'lrarr;': '\u21c6', - 'lrcorner;': '\u231f', - 'lrhar;': '\u21cb', - 'lrhard;': '\u296d', - 'lrm;': '\u200e', - 'lrtri;': '\u22bf', - 'lsaquo;': '\u2039', - 'Lscr;': '\u2112', - 'lscr;': '\U0001d4c1', - 'Lsh;': '\u21b0', - 'lsh;': '\u21b0', - 'lsim;': '\u2272', - 'lsime;': '\u2a8d', - 'lsimg;': '\u2a8f', - 'lsqb;': '[', - 'lsquo;': '\u2018', - 'lsquor;': '\u201a', - 'Lstrok;': '\u0141', - 'lstrok;': '\u0142', - 'LT': '<', - 'lt': '<', - 'LT;': '<', - 'Lt;': '\u226a', - 'lt;': '<', - 'ltcc;': '\u2aa6', - 'ltcir;': '\u2a79', - 'ltdot;': '\u22d6', - 'lthree;': '\u22cb', - 'ltimes;': '\u22c9', - 'ltlarr;': '\u2976', - 'ltquest;': '\u2a7b', - 'ltri;': '\u25c3', - 'ltrie;': '\u22b4', - 'ltrif;': '\u25c2', - 'ltrPar;': '\u2996', - 'lurdshar;': '\u294a', - 'luruhar;': '\u2966', - 'lvertneqq;': '\u2268\ufe00', - 'lvnE;': '\u2268\ufe00', - 'macr': '\xaf', - 'macr;': '\xaf', - 'male;': '\u2642', - 'malt;': '\u2720', - 'maltese;': '\u2720', - 'Map;': '\u2905', - 'map;': '\u21a6', - 'mapsto;': '\u21a6', - 'mapstodown;': '\u21a7', - 'mapstoleft;': '\u21a4', - 'mapstoup;': '\u21a5', - 'marker;': '\u25ae', - 'mcomma;': '\u2a29', - 'Mcy;': '\u041c', - 'mcy;': '\u043c', - 'mdash;': '\u2014', - 'mDDot;': '\u223a', - 'measuredangle;': '\u2221', - 'MediumSpace;': '\u205f', - 'Mellintrf;': '\u2133', - 'Mfr;': '\U0001d510', - 'mfr;': '\U0001d52a', - 'mho;': '\u2127', - 'micro': '\xb5', - 'micro;': '\xb5', - 'mid;': '\u2223', - 'midast;': '*', - 'midcir;': '\u2af0', - 'middot': '\xb7', - 'middot;': '\xb7', - 'minus;': '\u2212', - 'minusb;': '\u229f', - 'minusd;': '\u2238', - 'minusdu;': '\u2a2a', - 'MinusPlus;': '\u2213', - 'mlcp;': '\u2adb', - 'mldr;': '\u2026', - 'mnplus;': '\u2213', - 'models;': '\u22a7', - 'Mopf;': '\U0001d544', - 'mopf;': '\U0001d55e', - 'mp;': '\u2213', - 'Mscr;': '\u2133', - 'mscr;': '\U0001d4c2', - 'mstpos;': '\u223e', - 'Mu;': '\u039c', - 'mu;': '\u03bc', - 'multimap;': '\u22b8', - 'mumap;': '\u22b8', - 'nabla;': '\u2207', - 'Nacute;': '\u0143', - 'nacute;': '\u0144', - 'nang;': '\u2220\u20d2', - 'nap;': '\u2249', - 'napE;': '\u2a70\u0338', - 'napid;': '\u224b\u0338', - 'napos;': '\u0149', - 'napprox;': '\u2249', - 'natur;': '\u266e', - 'natural;': '\u266e', - 'naturals;': '\u2115', - 'nbsp': '\xa0', - 'nbsp;': '\xa0', - 'nbump;': '\u224e\u0338', - 'nbumpe;': '\u224f\u0338', - 'ncap;': '\u2a43', - 'Ncaron;': '\u0147', - 'ncaron;': '\u0148', - 'Ncedil;': '\u0145', - 'ncedil;': '\u0146', - 'ncong;': '\u2247', - 'ncongdot;': '\u2a6d\u0338', - 'ncup;': '\u2a42', - 'Ncy;': '\u041d', - 'ncy;': '\u043d', - 'ndash;': '\u2013', - 'ne;': '\u2260', - 'nearhk;': '\u2924', - 'neArr;': '\u21d7', - 'nearr;': '\u2197', - 'nearrow;': '\u2197', - 'nedot;': '\u2250\u0338', - 'NegativeMediumSpace;': '\u200b', - 'NegativeThickSpace;': '\u200b', - 'NegativeThinSpace;': '\u200b', - 'NegativeVeryThinSpace;': '\u200b', - 'nequiv;': '\u2262', - 'nesear;': '\u2928', - 'nesim;': '\u2242\u0338', - 'NestedGreaterGreater;': '\u226b', - 'NestedLessLess;': '\u226a', - 'NewLine;': '\n', - 'nexist;': '\u2204', - 'nexists;': '\u2204', - 'Nfr;': '\U0001d511', - 'nfr;': '\U0001d52b', - 'ngE;': '\u2267\u0338', - 'nge;': '\u2271', - 'ngeq;': '\u2271', - 'ngeqq;': '\u2267\u0338', - 'ngeqslant;': '\u2a7e\u0338', - 'nges;': '\u2a7e\u0338', - 'nGg;': '\u22d9\u0338', - 'ngsim;': '\u2275', - 'nGt;': '\u226b\u20d2', - 'ngt;': '\u226f', - 'ngtr;': '\u226f', - 'nGtv;': '\u226b\u0338', - 'nhArr;': '\u21ce', - 'nharr;': '\u21ae', - 'nhpar;': '\u2af2', - 'ni;': '\u220b', - 'nis;': '\u22fc', - 'nisd;': '\u22fa', - 'niv;': '\u220b', - 'NJcy;': '\u040a', - 'njcy;': '\u045a', - 'nlArr;': '\u21cd', - 'nlarr;': '\u219a', - 'nldr;': '\u2025', - 'nlE;': '\u2266\u0338', - 'nle;': '\u2270', - 'nLeftarrow;': '\u21cd', - 'nleftarrow;': '\u219a', - 'nLeftrightarrow;': '\u21ce', - 'nleftrightarrow;': '\u21ae', - 'nleq;': '\u2270', - 'nleqq;': '\u2266\u0338', - 'nleqslant;': '\u2a7d\u0338', - 'nles;': '\u2a7d\u0338', - 'nless;': '\u226e', - 'nLl;': '\u22d8\u0338', - 'nlsim;': '\u2274', - 'nLt;': '\u226a\u20d2', - 'nlt;': '\u226e', - 'nltri;': '\u22ea', - 'nltrie;': '\u22ec', - 'nLtv;': '\u226a\u0338', - 'nmid;': '\u2224', - 'NoBreak;': '\u2060', - 'NonBreakingSpace;': '\xa0', - 'Nopf;': '\u2115', - 'nopf;': '\U0001d55f', - 'not': '\xac', - 'Not;': '\u2aec', - 'not;': '\xac', - 'NotCongruent;': '\u2262', - 'NotCupCap;': '\u226d', - 'NotDoubleVerticalBar;': '\u2226', - 'NotElement;': '\u2209', - 'NotEqual;': '\u2260', - 'NotEqualTilde;': '\u2242\u0338', - 'NotExists;': '\u2204', - 'NotGreater;': '\u226f', - 'NotGreaterEqual;': '\u2271', - 'NotGreaterFullEqual;': '\u2267\u0338', - 'NotGreaterGreater;': '\u226b\u0338', - 'NotGreaterLess;': '\u2279', - 'NotGreaterSlantEqual;': '\u2a7e\u0338', - 'NotGreaterTilde;': '\u2275', - 'NotHumpDownHump;': '\u224e\u0338', - 'NotHumpEqual;': '\u224f\u0338', - 'notin;': '\u2209', - 'notindot;': '\u22f5\u0338', - 'notinE;': '\u22f9\u0338', - 'notinva;': '\u2209', - 'notinvb;': '\u22f7', - 'notinvc;': '\u22f6', - 'NotLeftTriangle;': '\u22ea', - 'NotLeftTriangleBar;': '\u29cf\u0338', - 'NotLeftTriangleEqual;': '\u22ec', - 'NotLess;': '\u226e', - 'NotLessEqual;': '\u2270', - 'NotLessGreater;': '\u2278', - 'NotLessLess;': '\u226a\u0338', - 'NotLessSlantEqual;': '\u2a7d\u0338', - 'NotLessTilde;': '\u2274', - 'NotNestedGreaterGreater;': '\u2aa2\u0338', - 'NotNestedLessLess;': '\u2aa1\u0338', - 'notni;': '\u220c', - 'notniva;': '\u220c', - 'notnivb;': '\u22fe', - 'notnivc;': '\u22fd', - 'NotPrecedes;': '\u2280', - 'NotPrecedesEqual;': '\u2aaf\u0338', - 'NotPrecedesSlantEqual;': '\u22e0', - 'NotReverseElement;': '\u220c', - 'NotRightTriangle;': '\u22eb', - 'NotRightTriangleBar;': '\u29d0\u0338', - 'NotRightTriangleEqual;': '\u22ed', - 'NotSquareSubset;': '\u228f\u0338', - 'NotSquareSubsetEqual;': '\u22e2', - 'NotSquareSuperset;': '\u2290\u0338', - 'NotSquareSupersetEqual;': '\u22e3', - 'NotSubset;': '\u2282\u20d2', - 'NotSubsetEqual;': '\u2288', - 'NotSucceeds;': '\u2281', - 'NotSucceedsEqual;': '\u2ab0\u0338', - 'NotSucceedsSlantEqual;': '\u22e1', - 'NotSucceedsTilde;': '\u227f\u0338', - 'NotSuperset;': '\u2283\u20d2', - 'NotSupersetEqual;': '\u2289', - 'NotTilde;': '\u2241', - 'NotTildeEqual;': '\u2244', - 'NotTildeFullEqual;': '\u2247', - 'NotTildeTilde;': '\u2249', - 'NotVerticalBar;': '\u2224', - 'npar;': '\u2226', - 'nparallel;': '\u2226', - 'nparsl;': '\u2afd\u20e5', - 'npart;': '\u2202\u0338', - 'npolint;': '\u2a14', - 'npr;': '\u2280', - 'nprcue;': '\u22e0', - 'npre;': '\u2aaf\u0338', - 'nprec;': '\u2280', - 'npreceq;': '\u2aaf\u0338', - 'nrArr;': '\u21cf', - 'nrarr;': '\u219b', - 'nrarrc;': '\u2933\u0338', - 'nrarrw;': '\u219d\u0338', - 'nRightarrow;': '\u21cf', - 'nrightarrow;': '\u219b', - 'nrtri;': '\u22eb', - 'nrtrie;': '\u22ed', - 'nsc;': '\u2281', - 'nsccue;': '\u22e1', - 'nsce;': '\u2ab0\u0338', - 'Nscr;': '\U0001d4a9', - 'nscr;': '\U0001d4c3', - 'nshortmid;': '\u2224', - 'nshortparallel;': '\u2226', - 'nsim;': '\u2241', - 'nsime;': '\u2244', - 'nsimeq;': '\u2244', - 'nsmid;': '\u2224', - 'nspar;': '\u2226', - 'nsqsube;': '\u22e2', - 'nsqsupe;': '\u22e3', - 'nsub;': '\u2284', - 'nsubE;': '\u2ac5\u0338', - 'nsube;': '\u2288', - 'nsubset;': '\u2282\u20d2', - 'nsubseteq;': '\u2288', - 'nsubseteqq;': '\u2ac5\u0338', - 'nsucc;': '\u2281', - 'nsucceq;': '\u2ab0\u0338', - 'nsup;': '\u2285', - 'nsupE;': '\u2ac6\u0338', - 'nsupe;': '\u2289', - 'nsupset;': '\u2283\u20d2', - 'nsupseteq;': '\u2289', - 'nsupseteqq;': '\u2ac6\u0338', - 'ntgl;': '\u2279', - 'Ntilde': '\xd1', - 'ntilde': '\xf1', - 'Ntilde;': '\xd1', - 'ntilde;': '\xf1', - 'ntlg;': '\u2278', - 'ntriangleleft;': '\u22ea', - 'ntrianglelefteq;': '\u22ec', - 'ntriangleright;': '\u22eb', - 'ntrianglerighteq;': '\u22ed', - 'Nu;': '\u039d', - 'nu;': '\u03bd', - 'num;': '#', - 'numero;': '\u2116', - 'numsp;': '\u2007', - 'nvap;': '\u224d\u20d2', - 'nVDash;': '\u22af', - 'nVdash;': '\u22ae', - 'nvDash;': '\u22ad', - 'nvdash;': '\u22ac', - 'nvge;': '\u2265\u20d2', - 'nvgt;': '>\u20d2', - 'nvHarr;': '\u2904', - 'nvinfin;': '\u29de', - 'nvlArr;': '\u2902', - 'nvle;': '\u2264\u20d2', - 'nvlt;': '<\u20d2', - 'nvltrie;': '\u22b4\u20d2', - 'nvrArr;': '\u2903', - 'nvrtrie;': '\u22b5\u20d2', - 'nvsim;': '\u223c\u20d2', - 'nwarhk;': '\u2923', - 'nwArr;': '\u21d6', - 'nwarr;': '\u2196', - 'nwarrow;': '\u2196', - 'nwnear;': '\u2927', - 'Oacute': '\xd3', - 'oacute': '\xf3', - 'Oacute;': '\xd3', - 'oacute;': '\xf3', - 'oast;': '\u229b', - 'ocir;': '\u229a', - 'Ocirc': '\xd4', - 'ocirc': '\xf4', - 'Ocirc;': '\xd4', - 'ocirc;': '\xf4', - 'Ocy;': '\u041e', - 'ocy;': '\u043e', - 'odash;': '\u229d', - 'Odblac;': '\u0150', - 'odblac;': '\u0151', - 'odiv;': '\u2a38', - 'odot;': '\u2299', - 'odsold;': '\u29bc', - 'OElig;': '\u0152', - 'oelig;': '\u0153', - 'ofcir;': '\u29bf', - 'Ofr;': '\U0001d512', - 'ofr;': '\U0001d52c', - 'ogon;': '\u02db', - 'Ograve': '\xd2', - 'ograve': '\xf2', - 'Ograve;': '\xd2', - 'ograve;': '\xf2', - 'ogt;': '\u29c1', - 'ohbar;': '\u29b5', - 'ohm;': '\u03a9', - 'oint;': '\u222e', - 'olarr;': '\u21ba', - 'olcir;': '\u29be', - 'olcross;': '\u29bb', - 'oline;': '\u203e', - 'olt;': '\u29c0', - 'Omacr;': '\u014c', - 'omacr;': '\u014d', - 'Omega;': '\u03a9', - 'omega;': '\u03c9', - 'Omicron;': '\u039f', - 'omicron;': '\u03bf', - 'omid;': '\u29b6', - 'ominus;': '\u2296', - 'Oopf;': '\U0001d546', - 'oopf;': '\U0001d560', - 'opar;': '\u29b7', - 'OpenCurlyDoubleQuote;': '\u201c', - 'OpenCurlyQuote;': '\u2018', - 'operp;': '\u29b9', - 'oplus;': '\u2295', - 'Or;': '\u2a54', - 'or;': '\u2228', - 'orarr;': '\u21bb', - 'ord;': '\u2a5d', - 'order;': '\u2134', - 'orderof;': '\u2134', - 'ordf': '\xaa', - 'ordf;': '\xaa', - 'ordm': '\xba', - 'ordm;': '\xba', - 'origof;': '\u22b6', - 'oror;': '\u2a56', - 'orslope;': '\u2a57', - 'orv;': '\u2a5b', - 'oS;': '\u24c8', - 'Oscr;': '\U0001d4aa', - 'oscr;': '\u2134', - 'Oslash': '\xd8', - 'oslash': '\xf8', - 'Oslash;': '\xd8', - 'oslash;': '\xf8', - 'osol;': '\u2298', - 'Otilde': '\xd5', - 'otilde': '\xf5', - 'Otilde;': '\xd5', - 'otilde;': '\xf5', - 'Otimes;': '\u2a37', - 'otimes;': '\u2297', - 'otimesas;': '\u2a36', - 'Ouml': '\xd6', - 'ouml': '\xf6', - 'Ouml;': '\xd6', - 'ouml;': '\xf6', - 'ovbar;': '\u233d', - 'OverBar;': '\u203e', - 'OverBrace;': '\u23de', - 'OverBracket;': '\u23b4', - 'OverParenthesis;': '\u23dc', - 'par;': '\u2225', - 'para': '\xb6', - 'para;': '\xb6', - 'parallel;': '\u2225', - 'parsim;': '\u2af3', - 'parsl;': '\u2afd', - 'part;': '\u2202', - 'PartialD;': '\u2202', - 'Pcy;': '\u041f', - 'pcy;': '\u043f', - 'percnt;': '%', - 'period;': '.', - 'permil;': '\u2030', - 'perp;': '\u22a5', - 'pertenk;': '\u2031', - 'Pfr;': '\U0001d513', - 'pfr;': '\U0001d52d', - 'Phi;': '\u03a6', - 'phi;': '\u03c6', - 'phiv;': '\u03d5', - 'phmmat;': '\u2133', - 'phone;': '\u260e', - 'Pi;': '\u03a0', - 'pi;': '\u03c0', - 'pitchfork;': '\u22d4', - 'piv;': '\u03d6', - 'planck;': '\u210f', - 'planckh;': '\u210e', - 'plankv;': '\u210f', - 'plus;': '+', - 'plusacir;': '\u2a23', - 'plusb;': '\u229e', - 'pluscir;': '\u2a22', - 'plusdo;': '\u2214', - 'plusdu;': '\u2a25', - 'pluse;': '\u2a72', - 'PlusMinus;': '\xb1', - 'plusmn': '\xb1', - 'plusmn;': '\xb1', - 'plussim;': '\u2a26', - 'plustwo;': '\u2a27', - 'pm;': '\xb1', - 'Poincareplane;': '\u210c', - 'pointint;': '\u2a15', - 'Popf;': '\u2119', - 'popf;': '\U0001d561', - 'pound': '\xa3', - 'pound;': '\xa3', - 'Pr;': '\u2abb', - 'pr;': '\u227a', - 'prap;': '\u2ab7', - 'prcue;': '\u227c', - 'prE;': '\u2ab3', - 'pre;': '\u2aaf', - 'prec;': '\u227a', - 'precapprox;': '\u2ab7', - 'preccurlyeq;': '\u227c', - 'Precedes;': '\u227a', - 'PrecedesEqual;': '\u2aaf', - 'PrecedesSlantEqual;': '\u227c', - 'PrecedesTilde;': '\u227e', - 'preceq;': '\u2aaf', - 'precnapprox;': '\u2ab9', - 'precneqq;': '\u2ab5', - 'precnsim;': '\u22e8', - 'precsim;': '\u227e', - 'Prime;': '\u2033', - 'prime;': '\u2032', - 'primes;': '\u2119', - 'prnap;': '\u2ab9', - 'prnE;': '\u2ab5', - 'prnsim;': '\u22e8', - 'prod;': '\u220f', - 'Product;': '\u220f', - 'profalar;': '\u232e', - 'profline;': '\u2312', - 'profsurf;': '\u2313', - 'prop;': '\u221d', - 'Proportion;': '\u2237', - 'Proportional;': '\u221d', - 'propto;': '\u221d', - 'prsim;': '\u227e', - 'prurel;': '\u22b0', - 'Pscr;': '\U0001d4ab', - 'pscr;': '\U0001d4c5', - 'Psi;': '\u03a8', - 'psi;': '\u03c8', - 'puncsp;': '\u2008', - 'Qfr;': '\U0001d514', - 'qfr;': '\U0001d52e', - 'qint;': '\u2a0c', - 'Qopf;': '\u211a', - 'qopf;': '\U0001d562', - 'qprime;': '\u2057', - 'Qscr;': '\U0001d4ac', - 'qscr;': '\U0001d4c6', - 'quaternions;': '\u210d', - 'quatint;': '\u2a16', - 'quest;': '?', - 'questeq;': '\u225f', - 'QUOT': '"', - 'quot': '"', - 'QUOT;': '"', - 'quot;': '"', - 'rAarr;': '\u21db', - 'race;': '\u223d\u0331', - 'Racute;': '\u0154', - 'racute;': '\u0155', - 'radic;': '\u221a', - 'raemptyv;': '\u29b3', - 'Rang;': '\u27eb', - 'rang;': '\u27e9', - 'rangd;': '\u2992', - 'range;': '\u29a5', - 'rangle;': '\u27e9', - 'raquo': '\xbb', - 'raquo;': '\xbb', - 'Rarr;': '\u21a0', - 'rArr;': '\u21d2', - 'rarr;': '\u2192', - 'rarrap;': '\u2975', - 'rarrb;': '\u21e5', - 'rarrbfs;': '\u2920', - 'rarrc;': '\u2933', - 'rarrfs;': '\u291e', - 'rarrhk;': '\u21aa', - 'rarrlp;': '\u21ac', - 'rarrpl;': '\u2945', - 'rarrsim;': '\u2974', - 'Rarrtl;': '\u2916', - 'rarrtl;': '\u21a3', - 'rarrw;': '\u219d', - 'rAtail;': '\u291c', - 'ratail;': '\u291a', - 'ratio;': '\u2236', - 'rationals;': '\u211a', - 'RBarr;': '\u2910', - 'rBarr;': '\u290f', - 'rbarr;': '\u290d', - 'rbbrk;': '\u2773', - 'rbrace;': '}', - 'rbrack;': ']', - 'rbrke;': '\u298c', - 'rbrksld;': '\u298e', - 'rbrkslu;': '\u2990', - 'Rcaron;': '\u0158', - 'rcaron;': '\u0159', - 'Rcedil;': '\u0156', - 'rcedil;': '\u0157', - 'rceil;': '\u2309', - 'rcub;': '}', - 'Rcy;': '\u0420', - 'rcy;': '\u0440', - 'rdca;': '\u2937', - 'rdldhar;': '\u2969', - 'rdquo;': '\u201d', - 'rdquor;': '\u201d', - 'rdsh;': '\u21b3', - 'Re;': '\u211c', - 'real;': '\u211c', - 'realine;': '\u211b', - 'realpart;': '\u211c', - 'reals;': '\u211d', - 'rect;': '\u25ad', - 'REG': '\xae', - 'reg': '\xae', - 'REG;': '\xae', - 'reg;': '\xae', - 'ReverseElement;': '\u220b', - 'ReverseEquilibrium;': '\u21cb', - 'ReverseUpEquilibrium;': '\u296f', - 'rfisht;': '\u297d', - 'rfloor;': '\u230b', - 'Rfr;': '\u211c', - 'rfr;': '\U0001d52f', - 'rHar;': '\u2964', - 'rhard;': '\u21c1', - 'rharu;': '\u21c0', - 'rharul;': '\u296c', - 'Rho;': '\u03a1', - 'rho;': '\u03c1', - 'rhov;': '\u03f1', - 'RightAngleBracket;': '\u27e9', - 'RightArrow;': '\u2192', - 'Rightarrow;': '\u21d2', - 'rightarrow;': '\u2192', - 'RightArrowBar;': '\u21e5', - 'RightArrowLeftArrow;': '\u21c4', - 'rightarrowtail;': '\u21a3', - 'RightCeiling;': '\u2309', - 'RightDoubleBracket;': '\u27e7', - 'RightDownTeeVector;': '\u295d', - 'RightDownVector;': '\u21c2', - 'RightDownVectorBar;': '\u2955', - 'RightFloor;': '\u230b', - 'rightharpoondown;': '\u21c1', - 'rightharpoonup;': '\u21c0', - 'rightleftarrows;': '\u21c4', - 'rightleftharpoons;': '\u21cc', - 'rightrightarrows;': '\u21c9', - 'rightsquigarrow;': '\u219d', - 'RightTee;': '\u22a2', - 'RightTeeArrow;': '\u21a6', - 'RightTeeVector;': '\u295b', - 'rightthreetimes;': '\u22cc', - 'RightTriangle;': '\u22b3', - 'RightTriangleBar;': '\u29d0', - 'RightTriangleEqual;': '\u22b5', - 'RightUpDownVector;': '\u294f', - 'RightUpTeeVector;': '\u295c', - 'RightUpVector;': '\u21be', - 'RightUpVectorBar;': '\u2954', - 'RightVector;': '\u21c0', - 'RightVectorBar;': '\u2953', - 'ring;': '\u02da', - 'risingdotseq;': '\u2253', - 'rlarr;': '\u21c4', - 'rlhar;': '\u21cc', - 'rlm;': '\u200f', - 'rmoust;': '\u23b1', - 'rmoustache;': '\u23b1', - 'rnmid;': '\u2aee', - 'roang;': '\u27ed', - 'roarr;': '\u21fe', - 'robrk;': '\u27e7', - 'ropar;': '\u2986', - 'Ropf;': '\u211d', - 'ropf;': '\U0001d563', - 'roplus;': '\u2a2e', - 'rotimes;': '\u2a35', - 'RoundImplies;': '\u2970', - 'rpar;': ')', - 'rpargt;': '\u2994', - 'rppolint;': '\u2a12', - 'rrarr;': '\u21c9', - 'Rrightarrow;': '\u21db', - 'rsaquo;': '\u203a', - 'Rscr;': '\u211b', - 'rscr;': '\U0001d4c7', - 'Rsh;': '\u21b1', - 'rsh;': '\u21b1', - 'rsqb;': ']', - 'rsquo;': '\u2019', - 'rsquor;': '\u2019', - 'rthree;': '\u22cc', - 'rtimes;': '\u22ca', - 'rtri;': '\u25b9', - 'rtrie;': '\u22b5', - 'rtrif;': '\u25b8', - 'rtriltri;': '\u29ce', - 'RuleDelayed;': '\u29f4', - 'ruluhar;': '\u2968', - 'rx;': '\u211e', - 'Sacute;': '\u015a', - 'sacute;': '\u015b', - 'sbquo;': '\u201a', - 'Sc;': '\u2abc', - 'sc;': '\u227b', - 'scap;': '\u2ab8', - 'Scaron;': '\u0160', - 'scaron;': '\u0161', - 'sccue;': '\u227d', - 'scE;': '\u2ab4', - 'sce;': '\u2ab0', - 'Scedil;': '\u015e', - 'scedil;': '\u015f', - 'Scirc;': '\u015c', - 'scirc;': '\u015d', - 'scnap;': '\u2aba', - 'scnE;': '\u2ab6', - 'scnsim;': '\u22e9', - 'scpolint;': '\u2a13', - 'scsim;': '\u227f', - 'Scy;': '\u0421', - 'scy;': '\u0441', - 'sdot;': '\u22c5', - 'sdotb;': '\u22a1', - 'sdote;': '\u2a66', - 'searhk;': '\u2925', - 'seArr;': '\u21d8', - 'searr;': '\u2198', - 'searrow;': '\u2198', - 'sect': '\xa7', - 'sect;': '\xa7', - 'semi;': ';', - 'seswar;': '\u2929', - 'setminus;': '\u2216', - 'setmn;': '\u2216', - 'sext;': '\u2736', - 'Sfr;': '\U0001d516', - 'sfr;': '\U0001d530', - 'sfrown;': '\u2322', - 'sharp;': '\u266f', - 'SHCHcy;': '\u0429', - 'shchcy;': '\u0449', - 'SHcy;': '\u0428', - 'shcy;': '\u0448', - 'ShortDownArrow;': '\u2193', - 'ShortLeftArrow;': '\u2190', - 'shortmid;': '\u2223', - 'shortparallel;': '\u2225', - 'ShortRightArrow;': '\u2192', - 'ShortUpArrow;': '\u2191', - 'shy': '\xad', - 'shy;': '\xad', - 'Sigma;': '\u03a3', - 'sigma;': '\u03c3', - 'sigmaf;': '\u03c2', - 'sigmav;': '\u03c2', - 'sim;': '\u223c', - 'simdot;': '\u2a6a', - 'sime;': '\u2243', - 'simeq;': '\u2243', - 'simg;': '\u2a9e', - 'simgE;': '\u2aa0', - 'siml;': '\u2a9d', - 'simlE;': '\u2a9f', - 'simne;': '\u2246', - 'simplus;': '\u2a24', - 'simrarr;': '\u2972', - 'slarr;': '\u2190', - 'SmallCircle;': '\u2218', - 'smallsetminus;': '\u2216', - 'smashp;': '\u2a33', - 'smeparsl;': '\u29e4', - 'smid;': '\u2223', - 'smile;': '\u2323', - 'smt;': '\u2aaa', - 'smte;': '\u2aac', - 'smtes;': '\u2aac\ufe00', - 'SOFTcy;': '\u042c', - 'softcy;': '\u044c', - 'sol;': '/', - 'solb;': '\u29c4', - 'solbar;': '\u233f', - 'Sopf;': '\U0001d54a', - 'sopf;': '\U0001d564', - 'spades;': '\u2660', - 'spadesuit;': '\u2660', - 'spar;': '\u2225', - 'sqcap;': '\u2293', - 'sqcaps;': '\u2293\ufe00', - 'sqcup;': '\u2294', - 'sqcups;': '\u2294\ufe00', - 'Sqrt;': '\u221a', - 'sqsub;': '\u228f', - 'sqsube;': '\u2291', - 'sqsubset;': '\u228f', - 'sqsubseteq;': '\u2291', - 'sqsup;': '\u2290', - 'sqsupe;': '\u2292', - 'sqsupset;': '\u2290', - 'sqsupseteq;': '\u2292', - 'squ;': '\u25a1', - 'Square;': '\u25a1', - 'square;': '\u25a1', - 'SquareIntersection;': '\u2293', - 'SquareSubset;': '\u228f', - 'SquareSubsetEqual;': '\u2291', - 'SquareSuperset;': '\u2290', - 'SquareSupersetEqual;': '\u2292', - 'SquareUnion;': '\u2294', - 'squarf;': '\u25aa', - 'squf;': '\u25aa', - 'srarr;': '\u2192', - 'Sscr;': '\U0001d4ae', - 'sscr;': '\U0001d4c8', - 'ssetmn;': '\u2216', - 'ssmile;': '\u2323', - 'sstarf;': '\u22c6', - 'Star;': '\u22c6', - 'star;': '\u2606', - 'starf;': '\u2605', - 'straightepsilon;': '\u03f5', - 'straightphi;': '\u03d5', - 'strns;': '\xaf', - 'Sub;': '\u22d0', - 'sub;': '\u2282', - 'subdot;': '\u2abd', - 'subE;': '\u2ac5', - 'sube;': '\u2286', - 'subedot;': '\u2ac3', - 'submult;': '\u2ac1', - 'subnE;': '\u2acb', - 'subne;': '\u228a', - 'subplus;': '\u2abf', - 'subrarr;': '\u2979', - 'Subset;': '\u22d0', - 'subset;': '\u2282', - 'subseteq;': '\u2286', - 'subseteqq;': '\u2ac5', - 'SubsetEqual;': '\u2286', - 'subsetneq;': '\u228a', - 'subsetneqq;': '\u2acb', - 'subsim;': '\u2ac7', - 'subsub;': '\u2ad5', - 'subsup;': '\u2ad3', - 'succ;': '\u227b', - 'succapprox;': '\u2ab8', - 'succcurlyeq;': '\u227d', - 'Succeeds;': '\u227b', - 'SucceedsEqual;': '\u2ab0', - 'SucceedsSlantEqual;': '\u227d', - 'SucceedsTilde;': '\u227f', - 'succeq;': '\u2ab0', - 'succnapprox;': '\u2aba', - 'succneqq;': '\u2ab6', - 'succnsim;': '\u22e9', - 'succsim;': '\u227f', - 'SuchThat;': '\u220b', - 'Sum;': '\u2211', - 'sum;': '\u2211', - 'sung;': '\u266a', - 'sup1': '\xb9', - 'sup1;': '\xb9', - 'sup2': '\xb2', - 'sup2;': '\xb2', - 'sup3': '\xb3', - 'sup3;': '\xb3', - 'Sup;': '\u22d1', - 'sup;': '\u2283', - 'supdot;': '\u2abe', - 'supdsub;': '\u2ad8', - 'supE;': '\u2ac6', - 'supe;': '\u2287', - 'supedot;': '\u2ac4', - 'Superset;': '\u2283', - 'SupersetEqual;': '\u2287', - 'suphsol;': '\u27c9', - 'suphsub;': '\u2ad7', - 'suplarr;': '\u297b', - 'supmult;': '\u2ac2', - 'supnE;': '\u2acc', - 'supne;': '\u228b', - 'supplus;': '\u2ac0', - 'Supset;': '\u22d1', - 'supset;': '\u2283', - 'supseteq;': '\u2287', - 'supseteqq;': '\u2ac6', - 'supsetneq;': '\u228b', - 'supsetneqq;': '\u2acc', - 'supsim;': '\u2ac8', - 'supsub;': '\u2ad4', - 'supsup;': '\u2ad6', - 'swarhk;': '\u2926', - 'swArr;': '\u21d9', - 'swarr;': '\u2199', - 'swarrow;': '\u2199', - 'swnwar;': '\u292a', - 'szlig': '\xdf', - 'szlig;': '\xdf', - 'Tab;': '\t', - 'target;': '\u2316', - 'Tau;': '\u03a4', - 'tau;': '\u03c4', - 'tbrk;': '\u23b4', - 'Tcaron;': '\u0164', - 'tcaron;': '\u0165', - 'Tcedil;': '\u0162', - 'tcedil;': '\u0163', - 'Tcy;': '\u0422', - 'tcy;': '\u0442', - 'tdot;': '\u20db', - 'telrec;': '\u2315', - 'Tfr;': '\U0001d517', - 'tfr;': '\U0001d531', - 'there4;': '\u2234', - 'Therefore;': '\u2234', - 'therefore;': '\u2234', - 'Theta;': '\u0398', - 'theta;': '\u03b8', - 'thetasym;': '\u03d1', - 'thetav;': '\u03d1', - 'thickapprox;': '\u2248', - 'thicksim;': '\u223c', - 'ThickSpace;': '\u205f\u200a', - 'thinsp;': '\u2009', - 'ThinSpace;': '\u2009', - 'thkap;': '\u2248', - 'thksim;': '\u223c', - 'THORN': '\xde', - 'thorn': '\xfe', - 'THORN;': '\xde', - 'thorn;': '\xfe', - 'Tilde;': '\u223c', - 'tilde;': '\u02dc', - 'TildeEqual;': '\u2243', - 'TildeFullEqual;': '\u2245', - 'TildeTilde;': '\u2248', - 'times': '\xd7', - 'times;': '\xd7', - 'timesb;': '\u22a0', - 'timesbar;': '\u2a31', - 'timesd;': '\u2a30', - 'tint;': '\u222d', - 'toea;': '\u2928', - 'top;': '\u22a4', - 'topbot;': '\u2336', - 'topcir;': '\u2af1', - 'Topf;': '\U0001d54b', - 'topf;': '\U0001d565', - 'topfork;': '\u2ada', - 'tosa;': '\u2929', - 'tprime;': '\u2034', - 'TRADE;': '\u2122', - 'trade;': '\u2122', - 'triangle;': '\u25b5', - 'triangledown;': '\u25bf', - 'triangleleft;': '\u25c3', - 'trianglelefteq;': '\u22b4', - 'triangleq;': '\u225c', - 'triangleright;': '\u25b9', - 'trianglerighteq;': '\u22b5', - 'tridot;': '\u25ec', - 'trie;': '\u225c', - 'triminus;': '\u2a3a', - 'TripleDot;': '\u20db', - 'triplus;': '\u2a39', - 'trisb;': '\u29cd', - 'tritime;': '\u2a3b', - 'trpezium;': '\u23e2', - 'Tscr;': '\U0001d4af', - 'tscr;': '\U0001d4c9', - 'TScy;': '\u0426', - 'tscy;': '\u0446', - 'TSHcy;': '\u040b', - 'tshcy;': '\u045b', - 'Tstrok;': '\u0166', - 'tstrok;': '\u0167', - 'twixt;': '\u226c', - 'twoheadleftarrow;': '\u219e', - 'twoheadrightarrow;': '\u21a0', - 'Uacute': '\xda', - 'uacute': '\xfa', - 'Uacute;': '\xda', - 'uacute;': '\xfa', - 'Uarr;': '\u219f', - 'uArr;': '\u21d1', - 'uarr;': '\u2191', - 'Uarrocir;': '\u2949', - 'Ubrcy;': '\u040e', - 'ubrcy;': '\u045e', - 'Ubreve;': '\u016c', - 'ubreve;': '\u016d', - 'Ucirc': '\xdb', - 'ucirc': '\xfb', - 'Ucirc;': '\xdb', - 'ucirc;': '\xfb', - 'Ucy;': '\u0423', - 'ucy;': '\u0443', - 'udarr;': '\u21c5', - 'Udblac;': '\u0170', - 'udblac;': '\u0171', - 'udhar;': '\u296e', - 'ufisht;': '\u297e', - 'Ufr;': '\U0001d518', - 'ufr;': '\U0001d532', - 'Ugrave': '\xd9', - 'ugrave': '\xf9', - 'Ugrave;': '\xd9', - 'ugrave;': '\xf9', - 'uHar;': '\u2963', - 'uharl;': '\u21bf', - 'uharr;': '\u21be', - 'uhblk;': '\u2580', - 'ulcorn;': '\u231c', - 'ulcorner;': '\u231c', - 'ulcrop;': '\u230f', - 'ultri;': '\u25f8', - 'Umacr;': '\u016a', - 'umacr;': '\u016b', - 'uml': '\xa8', - 'uml;': '\xa8', - 'UnderBar;': '_', - 'UnderBrace;': '\u23df', - 'UnderBracket;': '\u23b5', - 'UnderParenthesis;': '\u23dd', - 'Union;': '\u22c3', - 'UnionPlus;': '\u228e', - 'Uogon;': '\u0172', - 'uogon;': '\u0173', - 'Uopf;': '\U0001d54c', - 'uopf;': '\U0001d566', - 'UpArrow;': '\u2191', - 'Uparrow;': '\u21d1', - 'uparrow;': '\u2191', - 'UpArrowBar;': '\u2912', - 'UpArrowDownArrow;': '\u21c5', - 'UpDownArrow;': '\u2195', - 'Updownarrow;': '\u21d5', - 'updownarrow;': '\u2195', - 'UpEquilibrium;': '\u296e', - 'upharpoonleft;': '\u21bf', - 'upharpoonright;': '\u21be', - 'uplus;': '\u228e', - 'UpperLeftArrow;': '\u2196', - 'UpperRightArrow;': '\u2197', - 'Upsi;': '\u03d2', - 'upsi;': '\u03c5', - 'upsih;': '\u03d2', - 'Upsilon;': '\u03a5', - 'upsilon;': '\u03c5', - 'UpTee;': '\u22a5', - 'UpTeeArrow;': '\u21a5', - 'upuparrows;': '\u21c8', - 'urcorn;': '\u231d', - 'urcorner;': '\u231d', - 'urcrop;': '\u230e', - 'Uring;': '\u016e', - 'uring;': '\u016f', - 'urtri;': '\u25f9', - 'Uscr;': '\U0001d4b0', - 'uscr;': '\U0001d4ca', - 'utdot;': '\u22f0', - 'Utilde;': '\u0168', - 'utilde;': '\u0169', - 'utri;': '\u25b5', - 'utrif;': '\u25b4', - 'uuarr;': '\u21c8', - 'Uuml': '\xdc', - 'uuml': '\xfc', - 'Uuml;': '\xdc', - 'uuml;': '\xfc', - 'uwangle;': '\u29a7', - 'vangrt;': '\u299c', - 'varepsilon;': '\u03f5', - 'varkappa;': '\u03f0', - 'varnothing;': '\u2205', - 'varphi;': '\u03d5', - 'varpi;': '\u03d6', - 'varpropto;': '\u221d', - 'vArr;': '\u21d5', - 'varr;': '\u2195', - 'varrho;': '\u03f1', - 'varsigma;': '\u03c2', - 'varsubsetneq;': '\u228a\ufe00', - 'varsubsetneqq;': '\u2acb\ufe00', - 'varsupsetneq;': '\u228b\ufe00', - 'varsupsetneqq;': '\u2acc\ufe00', - 'vartheta;': '\u03d1', - 'vartriangleleft;': '\u22b2', - 'vartriangleright;': '\u22b3', - 'Vbar;': '\u2aeb', - 'vBar;': '\u2ae8', - 'vBarv;': '\u2ae9', - 'Vcy;': '\u0412', - 'vcy;': '\u0432', - 'VDash;': '\u22ab', - 'Vdash;': '\u22a9', - 'vDash;': '\u22a8', - 'vdash;': '\u22a2', - 'Vdashl;': '\u2ae6', - 'Vee;': '\u22c1', - 'vee;': '\u2228', - 'veebar;': '\u22bb', - 'veeeq;': '\u225a', - 'vellip;': '\u22ee', - 'Verbar;': '\u2016', - 'verbar;': '|', - 'Vert;': '\u2016', - 'vert;': '|', - 'VerticalBar;': '\u2223', - 'VerticalLine;': '|', - 'VerticalSeparator;': '\u2758', - 'VerticalTilde;': '\u2240', - 'VeryThinSpace;': '\u200a', - 'Vfr;': '\U0001d519', - 'vfr;': '\U0001d533', - 'vltri;': '\u22b2', - 'vnsub;': '\u2282\u20d2', - 'vnsup;': '\u2283\u20d2', - 'Vopf;': '\U0001d54d', - 'vopf;': '\U0001d567', - 'vprop;': '\u221d', - 'vrtri;': '\u22b3', - 'Vscr;': '\U0001d4b1', - 'vscr;': '\U0001d4cb', - 'vsubnE;': '\u2acb\ufe00', - 'vsubne;': '\u228a\ufe00', - 'vsupnE;': '\u2acc\ufe00', - 'vsupne;': '\u228b\ufe00', - 'Vvdash;': '\u22aa', - 'vzigzag;': '\u299a', - 'Wcirc;': '\u0174', - 'wcirc;': '\u0175', - 'wedbar;': '\u2a5f', - 'Wedge;': '\u22c0', - 'wedge;': '\u2227', - 'wedgeq;': '\u2259', - 'weierp;': '\u2118', - 'Wfr;': '\U0001d51a', - 'wfr;': '\U0001d534', - 'Wopf;': '\U0001d54e', - 'wopf;': '\U0001d568', - 'wp;': '\u2118', - 'wr;': '\u2240', - 'wreath;': '\u2240', - 'Wscr;': '\U0001d4b2', - 'wscr;': '\U0001d4cc', - 'xcap;': '\u22c2', - 'xcirc;': '\u25ef', - 'xcup;': '\u22c3', - 'xdtri;': '\u25bd', - 'Xfr;': '\U0001d51b', - 'xfr;': '\U0001d535', - 'xhArr;': '\u27fa', - 'xharr;': '\u27f7', - 'Xi;': '\u039e', - 'xi;': '\u03be', - 'xlArr;': '\u27f8', - 'xlarr;': '\u27f5', - 'xmap;': '\u27fc', - 'xnis;': '\u22fb', - 'xodot;': '\u2a00', - 'Xopf;': '\U0001d54f', - 'xopf;': '\U0001d569', - 'xoplus;': '\u2a01', - 'xotime;': '\u2a02', - 'xrArr;': '\u27f9', - 'xrarr;': '\u27f6', - 'Xscr;': '\U0001d4b3', - 'xscr;': '\U0001d4cd', - 'xsqcup;': '\u2a06', - 'xuplus;': '\u2a04', - 'xutri;': '\u25b3', - 'xvee;': '\u22c1', - 'xwedge;': '\u22c0', - 'Yacute': '\xdd', - 'yacute': '\xfd', - 'Yacute;': '\xdd', - 'yacute;': '\xfd', - 'YAcy;': '\u042f', - 'yacy;': '\u044f', - 'Ycirc;': '\u0176', - 'ycirc;': '\u0177', - 'Ycy;': '\u042b', - 'ycy;': '\u044b', - 'yen': '\xa5', - 'yen;': '\xa5', - 'Yfr;': '\U0001d51c', - 'yfr;': '\U0001d536', - 'YIcy;': '\u0407', - 'yicy;': '\u0457', - 'Yopf;': '\U0001d550', - 'yopf;': '\U0001d56a', - 'Yscr;': '\U0001d4b4', - 'yscr;': '\U0001d4ce', - 'YUcy;': '\u042e', - 'yucy;': '\u044e', - 'yuml': '\xff', - 'Yuml;': '\u0178', - 'yuml;': '\xff', - 'Zacute;': '\u0179', - 'zacute;': '\u017a', - 'Zcaron;': '\u017d', - 'zcaron;': '\u017e', - 'Zcy;': '\u0417', - 'zcy;': '\u0437', - 'Zdot;': '\u017b', - 'zdot;': '\u017c', - 'zeetrf;': '\u2128', - 'ZeroWidthSpace;': '\u200b', - 'Zeta;': '\u0396', - 'zeta;': '\u03b6', - 'Zfr;': '\u2128', - 'zfr;': '\U0001d537', - 'ZHcy;': '\u0416', - 'zhcy;': '\u0436', - 'zigrarr;': '\u21dd', - 'Zopf;': '\u2124', - 'zopf;': '\U0001d56b', - 'Zscr;': '\U0001d4b5', - 'zscr;': '\U0001d4cf', - 'zwj;': '\u200d', - 'zwnj;': '\u200c', -} - -# maps the Unicode codepoint to the HTML entity name -codepoint2name = {} - -# maps the HTML entity name to the character -# (or a character reference if the character is outside the Latin-1 range) -entitydefs = {} - -for (name, codepoint) in name2codepoint.items(): - codepoint2name[codepoint] = name - entitydefs[name] = chr(codepoint) - -del name, codepoint - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/html/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/html/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/html/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/html/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -""" -General functions for HTML manipulation, backported from Py3. - -Note that this uses Python 2.7 code with the corresponding Python 3 -module names and locations. -""" - -from __future__ import unicode_literals - - -_escape_map = {ord('&'): '&', ord('<'): '<', ord('>'): '>'} -_escape_map_full = {ord('&'): '&', ord('<'): '<', ord('>'): '>', - ord('"'): '"', ord('\''): '''} - -# NB: this is a candidate for a bytes/string polymorphic interface - -def escape(s, quote=True): - """ - Replace special characters "&", "<" and ">" to HTML-safe sequences. - If the optional flag quote is true (the default), the quotation mark - characters, both double quote (") and single quote (') characters are also - translated. - """ - assert not isinstance(s, bytes), 'Pass a unicode string' - if quote: - return s.translate(_escape_map_full) - return s.translate(_escape_map) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/html/parser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/html/parser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/html/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/html/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,537 +0,0 @@ -"""A parser for HTML and XHTML. - -Backported for python-future from Python 3.3. -""" - -# This file is based on sgmllib.py, but the API is slightly different. - -# XXX There should be a way to distinguish between PCDATA (parsed -# character data -- the normal case), RCDATA (replaceable character -# data -- only char and entity references and end tags are special) -# and CDATA (character data -- only end tags are special). - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import * -from future.backports import _markupbase -import re -import warnings - -# Regular expressions used for parsing - -interesting_normal = re.compile('[&<]') -incomplete = re.compile('&[a-zA-Z#]') - -entityref = re.compile('&([a-zA-Z][-.a-zA-Z0-9]*)[^a-zA-Z0-9]') -charref = re.compile('&#(?:[0-9]+|[xX][0-9a-fA-F]+)[^0-9a-fA-F]') - -starttagopen = re.compile('<[a-zA-Z]') -piclose = re.compile('>') -commentclose = re.compile(r'--\s*>') -tagfind = re.compile('([a-zA-Z][-.a-zA-Z0-9:_]*)(?:\s|/(?!>))*') -# see http://www.w3.org/TR/html5/tokenization.html#tag-open-state -# and http://www.w3.org/TR/html5/tokenization.html#tag-name-state -tagfind_tolerant = re.compile('[a-zA-Z][^\t\n\r\f />\x00]*') -# Note: -# 1) the strict attrfind isn't really strict, but we can't make it -# correctly strict without breaking backward compatibility; -# 2) if you change attrfind remember to update locatestarttagend too; -# 3) if you change attrfind and/or locatestarttagend the parser will -# explode, so don't do it. -attrfind = re.compile( - r'\s*([a-zA-Z_][-.:a-zA-Z_0-9]*)(\s*=\s*' - r'(\'[^\']*\'|"[^"]*"|[^\s"\'=<>`]*))?') -attrfind_tolerant = re.compile( - r'((?<=[\'"\s/])[^\s/>][^\s/=>]*)(\s*=+\s*' - r'(\'[^\']*\'|"[^"]*"|(?![\'"])[^>\s]*))?(?:\s|/(?!>))*') -locatestarttagend = re.compile(r""" - <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name - (?:\s+ # whitespace before attribute name - (?:[a-zA-Z_][-.:a-zA-Z0-9_]* # attribute name - (?:\s*=\s* # value indicator - (?:'[^']*' # LITA-enclosed value - |\"[^\"]*\" # LIT-enclosed value - |[^'\">\s]+ # bare value - ) - )? - ) - )* - \s* # trailing whitespace -""", re.VERBOSE) -locatestarttagend_tolerant = re.compile(r""" - <[a-zA-Z][-.a-zA-Z0-9:_]* # tag name - (?:[\s/]* # optional whitespace before attribute name - (?:(?<=['"\s/])[^\s/>][^\s/=>]* # attribute name - (?:\s*=+\s* # value indicator - (?:'[^']*' # LITA-enclosed value - |"[^"]*" # LIT-enclosed value - |(?!['"])[^>\s]* # bare value - ) - (?:\s*,)* # possibly followed by a comma - )?(?:\s|/(?!>))* - )* - )? - \s* # trailing whitespace -""", re.VERBOSE) -endendtag = re.compile('>') -# the HTML 5 spec, section 8.1.2.2, doesn't allow spaces between -# ') - - -class HTMLParseError(Exception): - """Exception raised for all parse errors.""" - - def __init__(self, msg, position=(None, None)): - assert msg - self.msg = msg - self.lineno = position[0] - self.offset = position[1] - - def __str__(self): - result = self.msg - if self.lineno is not None: - result = result + ", at line %d" % self.lineno - if self.offset is not None: - result = result + ", column %d" % (self.offset + 1) - return result - - -class HTMLParser(_markupbase.ParserBase): - """Find tags and other markup and call handler functions. - - Usage: - p = HTMLParser() - p.feed(data) - ... - p.close() - - Start tags are handled by calling self.handle_starttag() or - self.handle_startendtag(); end tags by self.handle_endtag(). The - data between tags is passed from the parser to the derived class - by calling self.handle_data() with the data as argument (the data - may be split up in arbitrary chunks). Entity references are - passed by calling self.handle_entityref() with the entity - reference as the argument. Numeric character references are - passed to self.handle_charref() with the string containing the - reference as the argument. - """ - - CDATA_CONTENT_ELEMENTS = ("script", "style") - - def __init__(self, strict=False): - """Initialize and reset this instance. - - If strict is set to False (the default) the parser will parse invalid - markup, otherwise it will raise an error. Note that the strict mode - is deprecated. - """ - if strict: - warnings.warn("The strict mode is deprecated.", - DeprecationWarning, stacklevel=2) - self.strict = strict - self.reset() - - def reset(self): - """Reset this instance. Loses all unprocessed data.""" - self.rawdata = '' - self.lasttag = '???' - self.interesting = interesting_normal - self.cdata_elem = None - _markupbase.ParserBase.reset(self) - - def feed(self, data): - r"""Feed data to the parser. - - Call this as often as you want, with as little or as much text - as you want (may include '\n'). - """ - self.rawdata = self.rawdata + data - self.goahead(0) - - def close(self): - """Handle any buffered data.""" - self.goahead(1) - - def error(self, message): - raise HTMLParseError(message, self.getpos()) - - __starttag_text = None - - def get_starttag_text(self): - """Return full source of start tag: '<...>'.""" - return self.__starttag_text - - def set_cdata_mode(self, elem): - self.cdata_elem = elem.lower() - self.interesting = re.compile(r'' % self.cdata_elem, re.I) - - def clear_cdata_mode(self): - self.interesting = interesting_normal - self.cdata_elem = None - - # Internal -- handle data as far as reasonable. May leave state - # and data to be processed by a subsequent call. If 'end' is - # true, force handling all data as if followed by EOF marker. - def goahead(self, end): - rawdata = self.rawdata - i = 0 - n = len(rawdata) - while i < n: - match = self.interesting.search(rawdata, i) # < or & - if match: - j = match.start() - else: - if self.cdata_elem: - break - j = n - if i < j: self.handle_data(rawdata[i:j]) - i = self.updatepos(i, j) - if i == n: break - startswith = rawdata.startswith - if startswith('<', i): - if starttagopen.match(rawdata, i): # < + letter - k = self.parse_starttag(i) - elif startswith("', i + 1) - if k < 0: - k = rawdata.find('<', i + 1) - if k < 0: - k = i + 1 - else: - k += 1 - self.handle_data(rawdata[i:k]) - i = self.updatepos(i, k) - elif startswith("&#", i): - match = charref.match(rawdata, i) - if match: - name = match.group()[2:-1] - self.handle_charref(name) - k = match.end() - if not startswith(';', k-1): - k = k - 1 - i = self.updatepos(i, k) - continue - else: - if ";" in rawdata[i:]: #bail by consuming &# - self.handle_data(rawdata[0:2]) - i = self.updatepos(i, 2) - break - elif startswith('&', i): - match = entityref.match(rawdata, i) - if match: - name = match.group(1) - self.handle_entityref(name) - k = match.end() - if not startswith(';', k-1): - k = k - 1 - i = self.updatepos(i, k) - continue - match = incomplete.match(rawdata, i) - if match: - # match.group() will contain at least 2 chars - if end and match.group() == rawdata[i:]: - if self.strict: - self.error("EOF in middle of entity or char ref") - else: - if k <= i: - k = n - i = self.updatepos(i, i + 1) - # incomplete - break - elif (i + 1) < n: - # not the end of the buffer, and can't be confused - # with some other construct - self.handle_data("&") - i = self.updatepos(i, i + 1) - else: - break - else: - assert 0, "interesting.search() lied" - # end while - if end and i < n and not self.cdata_elem: - self.handle_data(rawdata[i:n]) - i = self.updatepos(i, n) - self.rawdata = rawdata[i:] - - # Internal -- parse html declarations, return length or -1 if not terminated - # See w3.org/TR/html5/tokenization.html#markup-declaration-open-state - # See also parse_declaration in _markupbase - def parse_html_declaration(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == ' - gtpos = rawdata.find('>', i+9) - if gtpos == -1: - return -1 - self.handle_decl(rawdata[i+2:gtpos]) - return gtpos+1 - else: - return self.parse_bogus_comment(i) - - # Internal -- parse bogus comment, return length or -1 if not terminated - # see http://www.w3.org/TR/html5/tokenization.html#bogus-comment-state - def parse_bogus_comment(self, i, report=1): - rawdata = self.rawdata - assert rawdata[i:i+2] in ('', i+2) - if pos == -1: - return -1 - if report: - self.handle_comment(rawdata[i+2:pos]) - return pos + 1 - - # Internal -- parse processing instr, return end or -1 if not terminated - def parse_pi(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == ' - if not match: - return -1 - j = match.start() - self.handle_pi(rawdata[i+2: j]) - j = match.end() - return j - - # Internal -- handle starttag, return end or -1 if not terminated - def parse_starttag(self, i): - self.__starttag_text = None - endpos = self.check_for_whole_start_tag(i) - if endpos < 0: - return endpos - rawdata = self.rawdata - self.__starttag_text = rawdata[i:endpos] - - # Now parse the data between i+1 and j into a tag and attrs - attrs = [] - match = tagfind.match(rawdata, i+1) - assert match, 'unexpected call to parse_starttag()' - k = match.end() - self.lasttag = tag = match.group(1).lower() - while k < endpos: - if self.strict: - m = attrfind.match(rawdata, k) - else: - m = attrfind_tolerant.match(rawdata, k) - if not m: - break - attrname, rest, attrvalue = m.group(1, 2, 3) - if not rest: - attrvalue = None - elif attrvalue[:1] == '\'' == attrvalue[-1:] or \ - attrvalue[:1] == '"' == attrvalue[-1:]: - attrvalue = attrvalue[1:-1] - if attrvalue: - attrvalue = self.unescape(attrvalue) - attrs.append((attrname.lower(), attrvalue)) - k = m.end() - - end = rawdata[k:endpos].strip() - if end not in (">", "/>"): - lineno, offset = self.getpos() - if "\n" in self.__starttag_text: - lineno = lineno + self.__starttag_text.count("\n") - offset = len(self.__starttag_text) \ - - self.__starttag_text.rfind("\n") - else: - offset = offset + len(self.__starttag_text) - if self.strict: - self.error("junk characters in start tag: %r" - % (rawdata[k:endpos][:20],)) - self.handle_data(rawdata[i:endpos]) - return endpos - if end.endswith('/>'): - # XHTML-style empty tag: - self.handle_startendtag(tag, attrs) - else: - self.handle_starttag(tag, attrs) - if tag in self.CDATA_CONTENT_ELEMENTS: - self.set_cdata_mode(tag) - return endpos - - # Internal -- check to see if we have a complete starttag; return end - # or -1 if incomplete. - def check_for_whole_start_tag(self, i): - rawdata = self.rawdata - if self.strict: - m = locatestarttagend.match(rawdata, i) - else: - m = locatestarttagend_tolerant.match(rawdata, i) - if m: - j = m.end() - next = rawdata[j:j+1] - if next == ">": - return j + 1 - if next == "/": - if rawdata.startswith("/>", j): - return j + 2 - if rawdata.startswith("/", j): - # buffer boundary - return -1 - # else bogus input - if self.strict: - self.updatepos(i, j + 1) - self.error("malformed empty start tag") - if j > i: - return j - else: - return i + 1 - if next == "": - # end of input - return -1 - if next in ("abcdefghijklmnopqrstuvwxyz=/" - "ABCDEFGHIJKLMNOPQRSTUVWXYZ"): - # end of input in or before attribute value, or we have the - # '/' from a '/>' ending - return -1 - if self.strict: - self.updatepos(i, j) - self.error("malformed start tag") - if j > i: - return j - else: - return i + 1 - raise AssertionError("we should not get here!") - - # Internal -- parse endtag, return end or -1 if incomplete - def parse_endtag(self, i): - rawdata = self.rawdata - assert rawdata[i:i+2] == " - if not match: - return -1 - gtpos = match.end() - match = endtagfind.match(rawdata, i) # - if not match: - if self.cdata_elem is not None: - self.handle_data(rawdata[i:gtpos]) - return gtpos - if self.strict: - self.error("bad end tag: %r" % (rawdata[i:gtpos],)) - # find the name: w3.org/TR/html5/tokenization.html#tag-name-state - namematch = tagfind_tolerant.match(rawdata, i+2) - if not namematch: - # w3.org/TR/html5/tokenization.html#end-tag-open-state - if rawdata[i:i+3] == '': - return i+3 - else: - return self.parse_bogus_comment(i) - tagname = namematch.group().lower() - # consume and ignore other stuff between the name and the > - # Note: this is not 100% correct, since we might have things like - # , but looking for > after tha name should cover - # most of the cases and is much simpler - gtpos = rawdata.find('>', namematch.end()) - self.handle_endtag(tagname) - return gtpos+1 - - elem = match.group(1).lower() # script or style - if self.cdata_elem is not None: - if elem != self.cdata_elem: - self.handle_data(rawdata[i:gtpos]) - return gtpos - - self.handle_endtag(elem.lower()) - self.clear_cdata_mode() - return gtpos - - # Overridable -- finish processing of start+end tag: - def handle_startendtag(self, tag, attrs): - self.handle_starttag(tag, attrs) - self.handle_endtag(tag) - - # Overridable -- handle start tag - def handle_starttag(self, tag, attrs): - pass - - # Overridable -- handle end tag - def handle_endtag(self, tag): - pass - - # Overridable -- handle character reference - def handle_charref(self, name): - pass - - # Overridable -- handle entity reference - def handle_entityref(self, name): - pass - - # Overridable -- handle data - def handle_data(self, data): - pass - - # Overridable -- handle comment - def handle_comment(self, data): - pass - - # Overridable -- handle declaration - def handle_decl(self, decl): - pass - - # Overridable -- handle processing instruction - def handle_pi(self, data): - pass - - def unknown_decl(self, data): - if self.strict: - self.error("unknown declaration: %r" % (data,)) - - # Internal -- helper to remove special character quoting - def unescape(self, s): - if '&' not in s: - return s - def replaceEntities(s): - s = s.groups()[0] - try: - if s[0] == "#": - s = s[1:] - if s[0] in ['x','X']: - c = int(s[1:].rstrip(';'), 16) - else: - c = int(s.rstrip(';')) - return chr(c) - except ValueError: - return '&#' + s - else: - from future.backports.html.entities import html5 - if s in html5: - return html5[s] - elif s.endswith(';'): - return '&' + s - for x in range(2, len(s)): - if s[:x] in html5: - return html5[s[:x]] + s[x:] - else: - return '&' + s - - return re.sub(r"&(#?[xX]?(?:[0-9a-fA-F]+;|\w{1,32};?))", - replaceEntities, s) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/client.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/client.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1332 +0,0 @@ -"""HTTP/1.1 client library - -A backport of the Python 3.3 http/client.py module for python-future. - - - - -HTTPConnection goes through a number of "states", which define when a client -may legally make another request or fetch the response for a particular -request. This diagram details these state transitions: - - (null) - | - | HTTPConnection() - v - Idle - | - | putrequest() - v - Request-started - | - | ( putheader() )* endheaders() - v - Request-sent - | - | response = getresponse() - v - Unread-response [Response-headers-read] - |\____________________ - | | - | response.read() | putrequest() - v v - Idle Req-started-unread-response - ______/| - / | - response.read() | | ( putheader() )* endheaders() - v v - Request-started Req-sent-unread-response - | - | response.read() - v - Request-sent - -This diagram presents the following rules: - -- a second request may not be started until {response-headers-read} - -- a response [object] cannot be retrieved until {request-sent} - -- there is no differentiation between an unread response body and a - partially read response body - -Note: this enforcement is applied by the HTTPConnection class. The - HTTPResponse class does not enforce this state machine, which - implies sophisticated clients may accelerate the request/response - pipeline. Caution should be taken, though: accelerating the states - beyond the above pattern may imply knowledge of the server's - connection-close behavior for certain requests. For example, it - is impossible to tell whether the server will close the connection - UNTIL the response headers have been read; this means that further - requests cannot be placed into the pipeline until it is known that - the server will NOT be closing the connection. - -Logical State __state __response -------------- ------- ---------- -Idle _CS_IDLE None -Request-started _CS_REQ_STARTED None -Request-sent _CS_REQ_SENT None -Unread-response _CS_IDLE -Req-started-unread-response _CS_REQ_STARTED -Req-sent-unread-response _CS_REQ_SENT -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future.builtins import bytes, int, str, super -from future.utils import PY2 - -from future.backports.email import parser as email_parser -from future.backports.email import message as email_message -from future.backports.misc import create_connection as socket_create_connection -import io -import os -import socket -import collections -from future.backports.urllib.parse import urlsplit -import warnings -from array import array - -__all__ = ["HTTPResponse", "HTTPConnection", - "HTTPException", "NotConnected", "UnknownProtocol", - "UnknownTransferEncoding", "UnimplementedFileMode", - "IncompleteRead", "InvalidURL", "ImproperConnectionState", - "CannotSendRequest", "CannotSendHeader", "ResponseNotReady", - "BadStatusLine", "error", "responses"] - -HTTP_PORT = 80 -HTTPS_PORT = 443 - -_UNKNOWN = 'UNKNOWN' - -# connection states -_CS_IDLE = 'Idle' -_CS_REQ_STARTED = 'Request-started' -_CS_REQ_SENT = 'Request-sent' - -# status codes -# informational -CONTINUE = 100 -SWITCHING_PROTOCOLS = 101 -PROCESSING = 102 - -# successful -OK = 200 -CREATED = 201 -ACCEPTED = 202 -NON_AUTHORITATIVE_INFORMATION = 203 -NO_CONTENT = 204 -RESET_CONTENT = 205 -PARTIAL_CONTENT = 206 -MULTI_STATUS = 207 -IM_USED = 226 - -# redirection -MULTIPLE_CHOICES = 300 -MOVED_PERMANENTLY = 301 -FOUND = 302 -SEE_OTHER = 303 -NOT_MODIFIED = 304 -USE_PROXY = 305 -TEMPORARY_REDIRECT = 307 - -# client error -BAD_REQUEST = 400 -UNAUTHORIZED = 401 -PAYMENT_REQUIRED = 402 -FORBIDDEN = 403 -NOT_FOUND = 404 -METHOD_NOT_ALLOWED = 405 -NOT_ACCEPTABLE = 406 -PROXY_AUTHENTICATION_REQUIRED = 407 -REQUEST_TIMEOUT = 408 -CONFLICT = 409 -GONE = 410 -LENGTH_REQUIRED = 411 -PRECONDITION_FAILED = 412 -REQUEST_ENTITY_TOO_LARGE = 413 -REQUEST_URI_TOO_LONG = 414 -UNSUPPORTED_MEDIA_TYPE = 415 -REQUESTED_RANGE_NOT_SATISFIABLE = 416 -EXPECTATION_FAILED = 417 -UNPROCESSABLE_ENTITY = 422 -LOCKED = 423 -FAILED_DEPENDENCY = 424 -UPGRADE_REQUIRED = 426 -PRECONDITION_REQUIRED = 428 -TOO_MANY_REQUESTS = 429 -REQUEST_HEADER_FIELDS_TOO_LARGE = 431 - -# server error -INTERNAL_SERVER_ERROR = 500 -NOT_IMPLEMENTED = 501 -BAD_GATEWAY = 502 -SERVICE_UNAVAILABLE = 503 -GATEWAY_TIMEOUT = 504 -HTTP_VERSION_NOT_SUPPORTED = 505 -INSUFFICIENT_STORAGE = 507 -NOT_EXTENDED = 510 -NETWORK_AUTHENTICATION_REQUIRED = 511 - -# Mapping status codes to official W3C names -responses = { - 100: 'Continue', - 101: 'Switching Protocols', - - 200: 'OK', - 201: 'Created', - 202: 'Accepted', - 203: 'Non-Authoritative Information', - 204: 'No Content', - 205: 'Reset Content', - 206: 'Partial Content', - - 300: 'Multiple Choices', - 301: 'Moved Permanently', - 302: 'Found', - 303: 'See Other', - 304: 'Not Modified', - 305: 'Use Proxy', - 306: '(Unused)', - 307: 'Temporary Redirect', - - 400: 'Bad Request', - 401: 'Unauthorized', - 402: 'Payment Required', - 403: 'Forbidden', - 404: 'Not Found', - 405: 'Method Not Allowed', - 406: 'Not Acceptable', - 407: 'Proxy Authentication Required', - 408: 'Request Timeout', - 409: 'Conflict', - 410: 'Gone', - 411: 'Length Required', - 412: 'Precondition Failed', - 413: 'Request Entity Too Large', - 414: 'Request-URI Too Long', - 415: 'Unsupported Media Type', - 416: 'Requested Range Not Satisfiable', - 417: 'Expectation Failed', - 428: 'Precondition Required', - 429: 'Too Many Requests', - 431: 'Request Header Fields Too Large', - - 500: 'Internal Server Error', - 501: 'Not Implemented', - 502: 'Bad Gateway', - 503: 'Service Unavailable', - 504: 'Gateway Timeout', - 505: 'HTTP Version Not Supported', - 511: 'Network Authentication Required', -} - -# maximal amount of data to read at one time in _safe_read -MAXAMOUNT = 1048576 - -# maximal line length when calling readline(). -_MAXLINE = 65536 -_MAXHEADERS = 100 - - -class HTTPMessage(email_message.Message): - # XXX The only usage of this method is in - # http.server.CGIHTTPRequestHandler. Maybe move the code there so - # that it doesn't need to be part of the public API. The API has - # never been defined so this could cause backwards compatibility - # issues. - - def getallmatchingheaders(self, name): - """Find all header lines matching a given header name. - - Look through the list of headers and find all lines matching a given - header name (and their continuation lines). A list of the lines is - returned, without interpretation. If the header does not occur, an - empty list is returned. If the header occurs multiple times, all - occurrences are returned. Case is not important in the header name. - - """ - name = name.lower() + ':' - n = len(name) - lst = [] - hit = 0 - for line in self.keys(): - if line[:n].lower() == name: - hit = 1 - elif not line[:1].isspace(): - hit = 0 - if hit: - lst.append(line) - return lst - -def parse_headers(fp, _class=HTTPMessage): - """Parses only RFC2822 headers from a file pointer. - - email Parser wants to see strings rather than bytes. - But a TextIOWrapper around self.rfile would buffer too many bytes - from the stream, bytes which we later need to read as bytes. - So we read the correct bytes here, as bytes, for email Parser - to parse. - - """ - headers = [] - while True: - line = fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("header line") - headers.append(line) - if len(headers) > _MAXHEADERS: - raise HTTPException("got more than %d headers" % _MAXHEADERS) - if line in (b'\r\n', b'\n', b''): - break - hstring = bytes(b'').join(headers).decode('iso-8859-1') - return email_parser.Parser(_class=_class).parsestr(hstring) - - -_strict_sentinel = object() - -class HTTPResponse(io.RawIOBase): - - # See RFC 2616 sec 19.6 and RFC 1945 sec 6 for details. - - # The bytes from the socket object are iso-8859-1 strings. - # See RFC 2616 sec 2.2 which notes an exception for MIME-encoded - # text following RFC 2047. The basic status line parsing only - # accepts iso-8859-1. - - def __init__(self, sock, debuglevel=0, strict=_strict_sentinel, method=None, url=None): - # If the response includes a content-length header, we need to - # make sure that the client doesn't read more than the - # specified number of bytes. If it does, it will block until - # the server times out and closes the connection. This will - # happen if a self.fp.read() is done (without a size) whether - # self.fp is buffered or not. So, no self.fp.read() by - # clients unless they know what they are doing. - self.fp = sock.makefile("rb") - self.debuglevel = debuglevel - if strict is not _strict_sentinel: - warnings.warn("the 'strict' argument isn't supported anymore; " - "http.client now always assumes HTTP/1.x compliant servers.", - DeprecationWarning, 2) - self._method = method - - # The HTTPResponse object is returned via urllib. The clients - # of http and urllib expect different attributes for the - # headers. headers is used here and supports urllib. msg is - # provided as a backwards compatibility layer for http - # clients. - - self.headers = self.msg = None - - # from the Status-Line of the response - self.version = _UNKNOWN # HTTP-Version - self.status = _UNKNOWN # Status-Code - self.reason = _UNKNOWN # Reason-Phrase - - self.chunked = _UNKNOWN # is "chunked" being used? - self.chunk_left = _UNKNOWN # bytes left to read in current chunk - self.length = _UNKNOWN # number of bytes left in response - self.will_close = _UNKNOWN # conn will close at end of response - - def _read_status(self): - line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1") - if len(line) > _MAXLINE: - raise LineTooLong("status line") - if self.debuglevel > 0: - print("reply:", repr(line)) - if not line: - # Presumably, the server closed the connection before - # sending a valid response. - raise BadStatusLine(line) - try: - version, status, reason = line.split(None, 2) - except ValueError: - try: - version, status = line.split(None, 1) - reason = "" - except ValueError: - # empty version will cause next test to fail. - version = "" - if not version.startswith("HTTP/"): - self._close_conn() - raise BadStatusLine(line) - - # The status code is a three-digit number - try: - status = int(status) - if status < 100 or status > 999: - raise BadStatusLine(line) - except ValueError: - raise BadStatusLine(line) - return version, status, reason - - def begin(self): - if self.headers is not None: - # we've already started reading the response - return - - # read until we get a non-100 response - while True: - version, status, reason = self._read_status() - if status != CONTINUE: - break - # skip the header from the 100 response - while True: - skip = self.fp.readline(_MAXLINE + 1) - if len(skip) > _MAXLINE: - raise LineTooLong("header line") - skip = skip.strip() - if not skip: - break - if self.debuglevel > 0: - print("header:", skip) - - self.code = self.status = status - self.reason = reason.strip() - if version in ("HTTP/1.0", "HTTP/0.9"): - # Some servers might still return "0.9", treat it as 1.0 anyway - self.version = 10 - elif version.startswith("HTTP/1."): - self.version = 11 # use HTTP/1.1 code for HTTP/1.x where x>=1 - else: - raise UnknownProtocol(version) - - self.headers = self.msg = parse_headers(self.fp) - - if self.debuglevel > 0: - for hdr in self.headers: - print("header:", hdr, end=" ") - - # are we using the chunked-style of transfer encoding? - tr_enc = self.headers.get("transfer-encoding") - if tr_enc and tr_enc.lower() == "chunked": - self.chunked = True - self.chunk_left = None - else: - self.chunked = False - - # will the connection close at the end of the response? - self.will_close = self._check_close() - - # do we have a Content-Length? - # NOTE: RFC 2616, S4.4, #3 says we ignore this if tr_enc is "chunked" - self.length = None - length = self.headers.get("content-length") - - # are we using the chunked-style of transfer encoding? - tr_enc = self.headers.get("transfer-encoding") - if length and not self.chunked: - try: - self.length = int(length) - except ValueError: - self.length = None - else: - if self.length < 0: # ignore nonsensical negative lengths - self.length = None - else: - self.length = None - - # does the body have a fixed length? (of zero) - if (status == NO_CONTENT or status == NOT_MODIFIED or - 100 <= status < 200 or # 1xx codes - self._method == "HEAD"): - self.length = 0 - - # if the connection remains open, and we aren't using chunked, and - # a content-length was not provided, then assume that the connection - # WILL close. - if (not self.will_close and - not self.chunked and - self.length is None): - self.will_close = True - - def _check_close(self): - conn = self.headers.get("connection") - if self.version == 11: - # An HTTP/1.1 proxy is assumed to stay open unless - # explicitly closed. - conn = self.headers.get("connection") - if conn and "close" in conn.lower(): - return True - return False - - # Some HTTP/1.0 implementations have support for persistent - # connections, using rules different than HTTP/1.1. - - # For older HTTP, Keep-Alive indicates persistent connection. - if self.headers.get("keep-alive"): - return False - - # At least Akamai returns a "Connection: Keep-Alive" header, - # which was supposed to be sent by the client. - if conn and "keep-alive" in conn.lower(): - return False - - # Proxy-Connection is a netscape hack. - pconn = self.headers.get("proxy-connection") - if pconn and "keep-alive" in pconn.lower(): - return False - - # otherwise, assume it will close - return True - - def _close_conn(self): - fp = self.fp - self.fp = None - fp.close() - - def close(self): - super().close() # set "closed" flag - if self.fp: - self._close_conn() - - # These implementations are for the benefit of io.BufferedReader. - - # XXX This class should probably be revised to act more like - # the "raw stream" that BufferedReader expects. - - def flush(self): - super().flush() - if self.fp: - self.fp.flush() - - def readable(self): - return True - - # End of "raw stream" methods - - def isclosed(self): - """True if the connection is closed.""" - # NOTE: it is possible that we will not ever call self.close(). This - # case occurs when will_close is TRUE, length is None, and we - # read up to the last byte, but NOT past it. - # - # IMPLIES: if will_close is FALSE, then self.close() will ALWAYS be - # called, meaning self.isclosed() is meaningful. - return self.fp is None - - def read(self, amt=None): - if self.fp is None: - return bytes(b"") - - if self._method == "HEAD": - self._close_conn() - return bytes(b"") - - if amt is not None: - # Amount is given, so call base class version - # (which is implemented in terms of self.readinto) - return bytes(super(HTTPResponse, self).read(amt)) - else: - # Amount is not given (unbounded read) so we must check self.length - # and self.chunked - - if self.chunked: - return self._readall_chunked() - - if self.length is None: - s = self.fp.read() - else: - try: - s = self._safe_read(self.length) - except IncompleteRead: - self._close_conn() - raise - self.length = 0 - self._close_conn() # we read everything - return bytes(s) - - def readinto(self, b): - if self.fp is None: - return 0 - - if self._method == "HEAD": - self._close_conn() - return 0 - - if self.chunked: - return self._readinto_chunked(b) - - if self.length is not None: - if len(b) > self.length: - # clip the read to the "end of response" - b = memoryview(b)[0:self.length] - - # we do not use _safe_read() here because this may be a .will_close - # connection, and the user is reading more bytes than will be provided - # (for example, reading in 1k chunks) - - if PY2: - data = self.fp.read(len(b)) - n = len(data) - b[:n] = data - else: - n = self.fp.readinto(b) - - if not n and b: - # Ideally, we would raise IncompleteRead if the content-length - # wasn't satisfied, but it might break compatibility. - self._close_conn() - elif self.length is not None: - self.length -= n - if not self.length: - self._close_conn() - return n - - def _read_next_chunk_size(self): - # Read the next chunk size from the file - line = self.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("chunk size") - i = line.find(b";") - if i >= 0: - line = line[:i] # strip chunk-extensions - try: - return int(line, 16) - except ValueError: - # close the connection as protocol synchronisation is - # probably lost - self._close_conn() - raise - - def _read_and_discard_trailer(self): - # read and discard trailer up to the CRLF terminator - ### note: we shouldn't have any trailers! - while True: - line = self.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("trailer line") - if not line: - # a vanishingly small number of sites EOF without - # sending the trailer - break - if line in (b'\r\n', b'\n', b''): - break - - def _readall_chunked(self): - assert self.chunked != _UNKNOWN - chunk_left = self.chunk_left - value = [] - while True: - if chunk_left is None: - try: - chunk_left = self._read_next_chunk_size() - if chunk_left == 0: - break - except ValueError: - raise IncompleteRead(bytes(b'').join(value)) - value.append(self._safe_read(chunk_left)) - - # we read the whole chunk, get another - self._safe_read(2) # toss the CRLF at the end of the chunk - chunk_left = None - - self._read_and_discard_trailer() - - # we read everything; close the "file" - self._close_conn() - - return bytes(b'').join(value) - - def _readinto_chunked(self, b): - assert self.chunked != _UNKNOWN - chunk_left = self.chunk_left - - total_bytes = 0 - mvb = memoryview(b) - while True: - if chunk_left is None: - try: - chunk_left = self._read_next_chunk_size() - if chunk_left == 0: - break - except ValueError: - raise IncompleteRead(bytes(b[0:total_bytes])) - - if len(mvb) < chunk_left: - n = self._safe_readinto(mvb) - self.chunk_left = chunk_left - n - return total_bytes + n - elif len(mvb) == chunk_left: - n = self._safe_readinto(mvb) - self._safe_read(2) # toss the CRLF at the end of the chunk - self.chunk_left = None - return total_bytes + n - else: - temp_mvb = mvb[0:chunk_left] - n = self._safe_readinto(temp_mvb) - mvb = mvb[n:] - total_bytes += n - - # we read the whole chunk, get another - self._safe_read(2) # toss the CRLF at the end of the chunk - chunk_left = None - - self._read_and_discard_trailer() - - # we read everything; close the "file" - self._close_conn() - - return total_bytes - - def _safe_read(self, amt): - """Read the number of bytes requested, compensating for partial reads. - - Normally, we have a blocking socket, but a read() can be interrupted - by a signal (resulting in a partial read). - - Note that we cannot distinguish between EOF and an interrupt when zero - bytes have been read. IncompleteRead() will be raised in this - situation. - - This function should be used when bytes "should" be present for - reading. If the bytes are truly not available (due to EOF), then the - IncompleteRead exception can be used to detect the problem. - """ - s = [] - while amt > 0: - chunk = self.fp.read(min(amt, MAXAMOUNT)) - if not chunk: - raise IncompleteRead(bytes(b'').join(s), amt) - s.append(chunk) - amt -= len(chunk) - return bytes(b"").join(s) - - def _safe_readinto(self, b): - """Same as _safe_read, but for reading into a buffer.""" - total_bytes = 0 - mvb = memoryview(b) - while total_bytes < len(b): - if MAXAMOUNT < len(mvb): - temp_mvb = mvb[0:MAXAMOUNT] - n = self.fp.readinto(temp_mvb) - else: - n = self.fp.readinto(mvb) - if not n: - raise IncompleteRead(bytes(mvb[0:total_bytes]), len(b)) - mvb = mvb[n:] - total_bytes += n - return total_bytes - - def fileno(self): - return self.fp.fileno() - - def getheader(self, name, default=None): - if self.headers is None: - raise ResponseNotReady() - headers = self.headers.get_all(name) or default - if isinstance(headers, str) or not hasattr(headers, '__iter__'): - return headers - else: - return ', '.join(headers) - - def getheaders(self): - """Return list of (header, value) tuples.""" - if self.headers is None: - raise ResponseNotReady() - return list(self.headers.items()) - - # We override IOBase.__iter__ so that it doesn't check for closed-ness - - def __iter__(self): - return self - - # For compatibility with old-style urllib responses. - - def info(self): - return self.headers - - def geturl(self): - return self.url - - def getcode(self): - return self.status - -class HTTPConnection(object): - - _http_vsn = 11 - _http_vsn_str = 'HTTP/1.1' - - response_class = HTTPResponse - default_port = HTTP_PORT - auto_open = 1 - debuglevel = 0 - - def __init__(self, host, port=None, strict=_strict_sentinel, - timeout=socket._GLOBAL_DEFAULT_TIMEOUT, source_address=None): - if strict is not _strict_sentinel: - warnings.warn("the 'strict' argument isn't supported anymore; " - "http.client now always assumes HTTP/1.x compliant servers.", - DeprecationWarning, 2) - self.timeout = timeout - self.source_address = source_address - self.sock = None - self._buffer = [] - self.__response = None - self.__state = _CS_IDLE - self._method = None - self._tunnel_host = None - self._tunnel_port = None - self._tunnel_headers = {} - - self._set_hostport(host, port) - - def set_tunnel(self, host, port=None, headers=None): - """ Sets up the host and the port for the HTTP CONNECT Tunnelling. - - The headers argument should be a mapping of extra HTTP headers - to send with the CONNECT request. - """ - self._tunnel_host = host - self._tunnel_port = port - if headers: - self._tunnel_headers = headers - else: - self._tunnel_headers.clear() - - def _set_hostport(self, host, port): - if port is None: - i = host.rfind(':') - j = host.rfind(']') # ipv6 addresses have [...] - if i > j: - try: - port = int(host[i+1:]) - except ValueError: - if host[i+1:] == "": # http://foo.com:/ == http://foo.com/ - port = self.default_port - else: - raise InvalidURL("nonnumeric port: '%s'" % host[i+1:]) - host = host[:i] - else: - port = self.default_port - if host and host[0] == '[' and host[-1] == ']': - host = host[1:-1] - self.host = host - self.port = port - - def set_debuglevel(self, level): - self.debuglevel = level - - def _tunnel(self): - self._set_hostport(self._tunnel_host, self._tunnel_port) - connect_str = "CONNECT %s:%d HTTP/1.0\r\n" % (self.host, self.port) - connect_bytes = connect_str.encode("ascii") - self.send(connect_bytes) - for header, value in self._tunnel_headers.items(): - header_str = "%s: %s\r\n" % (header, value) - header_bytes = header_str.encode("latin-1") - self.send(header_bytes) - self.send(bytes(b'\r\n')) - - response = self.response_class(self.sock, method=self._method) - (version, code, message) = response._read_status() - - if code != 200: - self.close() - raise socket.error("Tunnel connection failed: %d %s" % (code, - message.strip())) - while True: - line = response.fp.readline(_MAXLINE + 1) - if len(line) > _MAXLINE: - raise LineTooLong("header line") - if not line: - # for sites which EOF without sending a trailer - break - if line in (b'\r\n', b'\n', b''): - break - - def connect(self): - """Connect to the host and port specified in __init__.""" - self.sock = socket_create_connection((self.host,self.port), - self.timeout, self.source_address) - if self._tunnel_host: - self._tunnel() - - def close(self): - """Close the connection to the HTTP server.""" - if self.sock: - self.sock.close() # close it manually... there may be other refs - self.sock = None - if self.__response: - self.__response.close() - self.__response = None - self.__state = _CS_IDLE - - def send(self, data): - """Send `data' to the server. - ``data`` can be a string object, a bytes object, an array object, a - file-like object that supports a .read() method, or an iterable object. - """ - - if self.sock is None: - if self.auto_open: - self.connect() - else: - raise NotConnected() - - if self.debuglevel > 0: - print("send:", repr(data)) - blocksize = 8192 - # Python 2.7 array objects have a read method which is incompatible - # with the 2-arg calling syntax below. - if hasattr(data, "read") and not isinstance(data, array): - if self.debuglevel > 0: - print("sendIng a read()able") - encode = False - try: - mode = data.mode - except AttributeError: - # io.BytesIO and other file-like objects don't have a `mode` - # attribute. - pass - else: - if "b" not in mode: - encode = True - if self.debuglevel > 0: - print("encoding file using iso-8859-1") - while 1: - datablock = data.read(blocksize) - if not datablock: - break - if encode: - datablock = datablock.encode("iso-8859-1") - self.sock.sendall(datablock) - return - try: - self.sock.sendall(data) - except TypeError: - if isinstance(data, collections.Iterable): - for d in data: - self.sock.sendall(d) - else: - raise TypeError("data should be a bytes-like object " - "or an iterable, got %r" % type(data)) - - def _output(self, s): - """Add a line of output to the current request buffer. - - Assumes that the line does *not* end with \\r\\n. - """ - self._buffer.append(s) - - def _send_output(self, message_body=None): - """Send the currently buffered request and clear the buffer. - - Appends an extra \\r\\n to the buffer. - A message_body may be specified, to be appended to the request. - """ - self._buffer.extend((bytes(b""), bytes(b""))) - msg = bytes(b"\r\n").join(self._buffer) - del self._buffer[:] - # If msg and message_body are sent in a single send() call, - # it will avoid performance problems caused by the interaction - # between delayed ack and the Nagle algorithm. - if isinstance(message_body, bytes): - msg += message_body - message_body = None - self.send(msg) - if message_body is not None: - # message_body was not a string (i.e. it is a file), and - # we must run the risk of Nagle. - self.send(message_body) - - def putrequest(self, method, url, skip_host=0, skip_accept_encoding=0): - """Send a request to the server. - - `method' specifies an HTTP request method, e.g. 'GET'. - `url' specifies the object being requested, e.g. '/index.html'. - `skip_host' if True does not add automatically a 'Host:' header - `skip_accept_encoding' if True does not add automatically an - 'Accept-Encoding:' header - """ - - # if a prior response has been completed, then forget about it. - if self.__response and self.__response.isclosed(): - self.__response = None - - - # in certain cases, we cannot issue another request on this connection. - # this occurs when: - # 1) we are in the process of sending a request. (_CS_REQ_STARTED) - # 2) a response to a previous request has signalled that it is going - # to close the connection upon completion. - # 3) the headers for the previous response have not been read, thus - # we cannot determine whether point (2) is true. (_CS_REQ_SENT) - # - # if there is no prior response, then we can request at will. - # - # if point (2) is true, then we will have passed the socket to the - # response (effectively meaning, "there is no prior response"), and - # will open a new one when a new request is made. - # - # Note: if a prior response exists, then we *can* start a new request. - # We are not allowed to begin fetching the response to this new - # request, however, until that prior response is complete. - # - if self.__state == _CS_IDLE: - self.__state = _CS_REQ_STARTED - else: - raise CannotSendRequest(self.__state) - - # Save the method we use, we need it later in the response phase - self._method = method - if not url: - url = '/' - request = '%s %s %s' % (method, url, self._http_vsn_str) - - # Non-ASCII characters should have been eliminated earlier - self._output(request.encode('ascii')) - - if self._http_vsn == 11: - # Issue some standard headers for better HTTP/1.1 compliance - - if not skip_host: - # this header is issued *only* for HTTP/1.1 - # connections. more specifically, this means it is - # only issued when the client uses the new - # HTTPConnection() class. backwards-compat clients - # will be using HTTP/1.0 and those clients may be - # issuing this header themselves. we should NOT issue - # it twice; some web servers (such as Apache) barf - # when they see two Host: headers - - # If we need a non-standard port,include it in the - # header. If the request is going through a proxy, - # but the host of the actual URL, not the host of the - # proxy. - - netloc = '' - if url.startswith('http'): - nil, netloc, nil, nil, nil = urlsplit(url) - - if netloc: - try: - netloc_enc = netloc.encode("ascii") - except UnicodeEncodeError: - netloc_enc = netloc.encode("idna") - self.putheader('Host', netloc_enc) - else: - try: - host_enc = self.host.encode("ascii") - except UnicodeEncodeError: - host_enc = self.host.encode("idna") - - # As per RFC 273, IPv6 address should be wrapped with [] - # when used as Host header - - if self.host.find(':') >= 0: - host_enc = bytes(b'[' + host_enc + b']') - - if self.port == self.default_port: - self.putheader('Host', host_enc) - else: - host_enc = host_enc.decode("ascii") - self.putheader('Host', "%s:%s" % (host_enc, self.port)) - - # note: we are assuming that clients will not attempt to set these - # headers since *this* library must deal with the - # consequences. this also means that when the supporting - # libraries are updated to recognize other forms, then this - # code should be changed (removed or updated). - - # we only want a Content-Encoding of "identity" since we don't - # support encodings such as x-gzip or x-deflate. - if not skip_accept_encoding: - self.putheader('Accept-Encoding', 'identity') - - # we can accept "chunked" Transfer-Encodings, but no others - # NOTE: no TE header implies *only* "chunked" - #self.putheader('TE', 'chunked') - - # if TE is supplied in the header, then it must appear in a - # Connection header. - #self.putheader('Connection', 'TE') - - else: - # For HTTP/1.0, the server will assume "not chunked" - pass - - def putheader(self, header, *values): - """Send a request header line to the server. - - For example: h.putheader('Accept', 'text/html') - """ - if self.__state != _CS_REQ_STARTED: - raise CannotSendHeader() - - if hasattr(header, 'encode'): - header = header.encode('ascii') - values = list(values) - for i, one_value in enumerate(values): - if hasattr(one_value, 'encode'): - values[i] = one_value.encode('latin-1') - elif isinstance(one_value, int): - values[i] = str(one_value).encode('ascii') - value = bytes(b'\r\n\t').join(values) - header = header + bytes(b': ') + value - self._output(header) - - def endheaders(self, message_body=None): - """Indicate that the last header line has been sent to the server. - - This method sends the request to the server. The optional message_body - argument can be used to pass a message body associated with the - request. The message body will be sent in the same packet as the - message headers if it is a string, otherwise it is sent as a separate - packet. - """ - if self.__state == _CS_REQ_STARTED: - self.__state = _CS_REQ_SENT - else: - raise CannotSendHeader() - self._send_output(message_body) - - def request(self, method, url, body=None, headers={}): - """Send a complete request to the server.""" - self._send_request(method, url, body, headers) - - def _set_content_length(self, body): - # Set the content-length based on the body. - thelen = None - try: - thelen = str(len(body)) - except TypeError as te: - # If this is a file-like object, try to - # fstat its file descriptor - try: - thelen = str(os.fstat(body.fileno()).st_size) - except (AttributeError, OSError): - # Don't send a length if this failed - if self.debuglevel > 0: print("Cannot stat!!") - - if thelen is not None: - self.putheader('Content-Length', thelen) - - def _send_request(self, method, url, body, headers): - # Honor explicitly requested Host: and Accept-Encoding: headers. - header_names = dict.fromkeys([k.lower() for k in headers]) - skips = {} - if 'host' in header_names: - skips['skip_host'] = 1 - if 'accept-encoding' in header_names: - skips['skip_accept_encoding'] = 1 - - self.putrequest(method, url, **skips) - - if body is not None and ('content-length' not in header_names): - self._set_content_length(body) - for hdr, value in headers.items(): - self.putheader(hdr, value) - if isinstance(body, str): - # RFC 2616 Section 3.7.1 says that text default has a - # default charset of iso-8859-1. - body = body.encode('iso-8859-1') - self.endheaders(body) - - def getresponse(self): - """Get the response from the server. - - If the HTTPConnection is in the correct state, returns an - instance of HTTPResponse or of whatever object is returned by - class the response_class variable. - - If a request has not been sent or if a previous response has - not be handled, ResponseNotReady is raised. If the HTTP - response indicates that the connection should be closed, then - it will be closed before the response is returned. When the - connection is closed, the underlying socket is closed. - """ - - # if a prior response has been completed, then forget about it. - if self.__response and self.__response.isclosed(): - self.__response = None - - # if a prior response exists, then it must be completed (otherwise, we - # cannot read this response's header to determine the connection-close - # behavior) - # - # note: if a prior response existed, but was connection-close, then the - # socket and response were made independent of this HTTPConnection - # object since a new request requires that we open a whole new - # connection - # - # this means the prior response had one of two states: - # 1) will_close: this connection was reset and the prior socket and - # response operate independently - # 2) persistent: the response was retained and we await its - # isclosed() status to become true. - # - if self.__state != _CS_REQ_SENT or self.__response: - raise ResponseNotReady(self.__state) - - if self.debuglevel > 0: - response = self.response_class(self.sock, self.debuglevel, - method=self._method) - else: - response = self.response_class(self.sock, method=self._method) - - response.begin() - assert response.will_close != _UNKNOWN - self.__state = _CS_IDLE - - if response.will_close: - # this effectively passes the connection to the response - self.close() - else: - # remember this, so we can tell when it is complete - self.__response = response - - return response - -try: - import ssl - from ssl import SSLContext -except ImportError: - pass -else: - class HTTPSConnection(HTTPConnection): - "This class allows communication via SSL." - - default_port = HTTPS_PORT - - # XXX Should key_file and cert_file be deprecated in favour of context? - - def __init__(self, host, port=None, key_file=None, cert_file=None, - strict=_strict_sentinel, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - source_address=None, **_3to2kwargs): - if 'check_hostname' in _3to2kwargs: check_hostname = _3to2kwargs['check_hostname']; del _3to2kwargs['check_hostname'] - else: check_hostname = None - if 'context' in _3to2kwargs: context = _3to2kwargs['context']; del _3to2kwargs['context'] - else: context = None - super(HTTPSConnection, self).__init__(host, port, strict, timeout, - source_address) - self.key_file = key_file - self.cert_file = cert_file - if context is None: - # Some reasonable defaults - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - will_verify = context.verify_mode != ssl.CERT_NONE - if check_hostname is None: - check_hostname = will_verify - elif check_hostname and not will_verify: - raise ValueError("check_hostname needs a SSL context with " - "either CERT_OPTIONAL or CERT_REQUIRED") - if key_file or cert_file: - context.load_cert_chain(cert_file, key_file) - self._context = context - self._check_hostname = check_hostname - - def connect(self): - "Connect to a host on a given (SSL) port." - - sock = socket_create_connection((self.host, self.port), - self.timeout, self.source_address) - - if self._tunnel_host: - self.sock = sock - self._tunnel() - - server_hostname = self.host if ssl.HAS_SNI else None - self.sock = self._context.wrap_socket(sock, - server_hostname=server_hostname) - try: - if self._check_hostname: - ssl.match_hostname(self.sock.getpeercert(), self.host) - except Exception: - self.sock.shutdown(socket.SHUT_RDWR) - self.sock.close() - raise - - __all__.append("HTTPSConnection") - - - # ###################################### - # # We use the old HTTPSConnection class from Py2.7, because ssl.SSLContext - # # doesn't exist in the Py2.7 stdlib - # class HTTPSConnection(HTTPConnection): - # "This class allows communication via SSL." - - # default_port = HTTPS_PORT - - # def __init__(self, host, port=None, key_file=None, cert_file=None, - # strict=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, - # source_address=None): - # HTTPConnection.__init__(self, host, port, strict, timeout, - # source_address) - # self.key_file = key_file - # self.cert_file = cert_file - - # def connect(self): - # "Connect to a host on a given (SSL) port." - - # sock = socket_create_connection((self.host, self.port), - # self.timeout, self.source_address) - # if self._tunnel_host: - # self.sock = sock - # self._tunnel() - # self.sock = ssl.wrap_socket(sock, self.key_file, self.cert_file) - - # __all__.append("HTTPSConnection") - # ###################################### - - -class HTTPException(Exception): - # Subclasses that define an __init__ must call Exception.__init__ - # or define self.args. Otherwise, str() will fail. - pass - -class NotConnected(HTTPException): - pass - -class InvalidURL(HTTPException): - pass - -class UnknownProtocol(HTTPException): - def __init__(self, version): - self.args = version, - self.version = version - -class UnknownTransferEncoding(HTTPException): - pass - -class UnimplementedFileMode(HTTPException): - pass - -class IncompleteRead(HTTPException): - def __init__(self, partial, expected=None): - self.args = partial, - self.partial = partial - self.expected = expected - def __repr__(self): - if self.expected is not None: - e = ', %i more expected' % self.expected - else: - e = '' - return 'IncompleteRead(%i bytes read%s)' % (len(self.partial), e) - def __str__(self): - return repr(self) - -class ImproperConnectionState(HTTPException): - pass - -class CannotSendRequest(ImproperConnectionState): - pass - -class CannotSendHeader(ImproperConnectionState): - pass - -class ResponseNotReady(ImproperConnectionState): - pass - -class BadStatusLine(HTTPException): - def __init__(self, line): - if not line: - line = repr(line) - self.args = line, - self.line = line - -class LineTooLong(HTTPException): - def __init__(self, line_type): - HTTPException.__init__(self, "got more than %d bytes when reading %s" - % (_MAXLINE, line_type)) - -# for backwards compatibility -error = HTTPException diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/cookiejar.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/cookiejar.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/cookiejar.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/cookiejar.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2109 +0,0 @@ -r"""HTTP cookie handling for web clients. - -This is a backport of the Py3.3 ``http.cookiejar`` module for -python-future. - -This module has (now fairly distant) origins in Gisle Aas' Perl module -HTTP::Cookies, from the libwww-perl library. - -Docstrings, comments and debug strings in this code refer to the -attributes of the HTTP cookie system as cookie-attributes, to distinguish -them clearly from Python attributes. - -Class diagram (note that BSDDBCookieJar and the MSIE* classes are not -distributed with the Python standard library, but are available from -http://wwwsearch.sf.net/): - - CookieJar____ - / \ \ - FileCookieJar \ \ - / | \ \ \ - MozillaCookieJar | LWPCookieJar \ \ - | | \ - | ---MSIEBase | \ - | / | | \ - | / MSIEDBCookieJar BSDDBCookieJar - |/ - MSIECookieJar - -""" - -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import filter, int, map, open, str -from future.utils import as_native_str - -__all__ = ['Cookie', 'CookieJar', 'CookiePolicy', 'DefaultCookiePolicy', - 'FileCookieJar', 'LWPCookieJar', 'LoadError', 'MozillaCookieJar'] - -import copy -import datetime -import re -re.ASCII = 0 -import time -from future.backports.urllib.parse import urlparse, urlsplit, quote -from future.backports.http.client import HTTP_PORT -try: - import threading as _threading -except ImportError: - import dummy_threading as _threading -from calendar import timegm - -debug = False # set to True to enable debugging via the logging module -logger = None - -def _debug(*args): - if not debug: - return - global logger - if not logger: - import logging - logger = logging.getLogger("http.cookiejar") - return logger.debug(*args) - - -DEFAULT_HTTP_PORT = str(HTTP_PORT) -MISSING_FILENAME_TEXT = ("a filename was not supplied (nor was the CookieJar " - "instance initialised with one)") - -def _warn_unhandled_exception(): - # There are a few catch-all except: statements in this module, for - # catching input that's bad in unexpected ways. Warn if any - # exceptions are caught there. - import io, warnings, traceback - f = io.StringIO() - traceback.print_exc(None, f) - msg = f.getvalue() - warnings.warn("http.cookiejar bug!\n%s" % msg, stacklevel=2) - - -# Date/time conversion -# ----------------------------------------------------------------------------- - -EPOCH_YEAR = 1970 -def _timegm(tt): - year, month, mday, hour, min, sec = tt[:6] - if ((year >= EPOCH_YEAR) and (1 <= month <= 12) and (1 <= mday <= 31) and - (0 <= hour <= 24) and (0 <= min <= 59) and (0 <= sec <= 61)): - return timegm(tt) - else: - return None - -DAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"] -MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", - "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] -MONTHS_LOWER = [] -for month in MONTHS: MONTHS_LOWER.append(month.lower()) - -def time2isoz(t=None): - """Return a string representing time in seconds since epoch, t. - - If the function is called without an argument, it will use the current - time. - - The format of the returned string is like "YYYY-MM-DD hh:mm:ssZ", - representing Universal Time (UTC, aka GMT). An example of this format is: - - 1994-11-24 08:49:37Z - - """ - if t is None: - dt = datetime.datetime.utcnow() - else: - dt = datetime.datetime.utcfromtimestamp(t) - return "%04d-%02d-%02d %02d:%02d:%02dZ" % ( - dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second) - -def time2netscape(t=None): - """Return a string representing time in seconds since epoch, t. - - If the function is called without an argument, it will use the current - time. - - The format of the returned string is like this: - - Wed, DD-Mon-YYYY HH:MM:SS GMT - - """ - if t is None: - dt = datetime.datetime.utcnow() - else: - dt = datetime.datetime.utcfromtimestamp(t) - return "%s %02d-%s-%04d %02d:%02d:%02d GMT" % ( - DAYS[dt.weekday()], dt.day, MONTHS[dt.month-1], - dt.year, dt.hour, dt.minute, dt.second) - - -UTC_ZONES = {"GMT": None, "UTC": None, "UT": None, "Z": None} - -TIMEZONE_RE = re.compile(r"^([-+])?(\d\d?):?(\d\d)?$", re.ASCII) -def offset_from_tz_string(tz): - offset = None - if tz in UTC_ZONES: - offset = 0 - else: - m = TIMEZONE_RE.search(tz) - if m: - offset = 3600 * int(m.group(2)) - if m.group(3): - offset = offset + 60 * int(m.group(3)) - if m.group(1) == '-': - offset = -offset - return offset - -def _str2time(day, mon, yr, hr, min, sec, tz): - # translate month name to number - # month numbers start with 1 (January) - try: - mon = MONTHS_LOWER.index(mon.lower())+1 - except ValueError: - # maybe it's already a number - try: - imon = int(mon) - except ValueError: - return None - if 1 <= imon <= 12: - mon = imon - else: - return None - - # make sure clock elements are defined - if hr is None: hr = 0 - if min is None: min = 0 - if sec is None: sec = 0 - - yr = int(yr) - day = int(day) - hr = int(hr) - min = int(min) - sec = int(sec) - - if yr < 1000: - # find "obvious" year - cur_yr = time.localtime(time.time())[0] - m = cur_yr % 100 - tmp = yr - yr = yr + cur_yr - m - m = m - tmp - if abs(m) > 50: - if m > 0: yr = yr + 100 - else: yr = yr - 100 - - # convert UTC time tuple to seconds since epoch (not timezone-adjusted) - t = _timegm((yr, mon, day, hr, min, sec, tz)) - - if t is not None: - # adjust time using timezone string, to get absolute time since epoch - if tz is None: - tz = "UTC" - tz = tz.upper() - offset = offset_from_tz_string(tz) - if offset is None: - return None - t = t - offset - - return t - -STRICT_DATE_RE = re.compile( - r"^[SMTWF][a-z][a-z], (\d\d) ([JFMASOND][a-z][a-z]) " - "(\d\d\d\d) (\d\d):(\d\d):(\d\d) GMT$", re.ASCII) -WEEKDAY_RE = re.compile( - r"^(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)[a-z]*,?\s*", re.I | re.ASCII) -LOOSE_HTTP_DATE_RE = re.compile( - r"""^ - (\d\d?) # day - (?:\s+|[-\/]) - (\w+) # month - (?:\s+|[-\/]) - (\d+) # year - (?: - (?:\s+|:) # separator before clock - (\d\d?):(\d\d) # hour:min - (?::(\d\d))? # optional seconds - )? # optional clock - \s* - ([-+]?\d{2,4}|(?![APap][Mm]\b)[A-Za-z]+)? # timezone - \s* - (?:\(\w+\))? # ASCII representation of timezone in parens. - \s*$""", re.X | re.ASCII) -def http2time(text): - """Returns time in seconds since epoch of time represented by a string. - - Return value is an integer. - - None is returned if the format of str is unrecognized, the time is outside - the representable range, or the timezone string is not recognized. If the - string contains no timezone, UTC is assumed. - - The timezone in the string may be numerical (like "-0800" or "+0100") or a - string timezone (like "UTC", "GMT", "BST" or "EST"). Currently, only the - timezone strings equivalent to UTC (zero offset) are known to the function. - - The function loosely parses the following formats: - - Wed, 09 Feb 1994 22:23:32 GMT -- HTTP format - Tuesday, 08-Feb-94 14:15:29 GMT -- old rfc850 HTTP format - Tuesday, 08-Feb-1994 14:15:29 GMT -- broken rfc850 HTTP format - 09 Feb 1994 22:23:32 GMT -- HTTP format (no weekday) - 08-Feb-94 14:15:29 GMT -- rfc850 format (no weekday) - 08-Feb-1994 14:15:29 GMT -- broken rfc850 format (no weekday) - - The parser ignores leading and trailing whitespace. The time may be - absent. - - If the year is given with only 2 digits, the function will select the - century that makes the year closest to the current date. - - """ - # fast exit for strictly conforming string - m = STRICT_DATE_RE.search(text) - if m: - g = m.groups() - mon = MONTHS_LOWER.index(g[1].lower()) + 1 - tt = (int(g[2]), mon, int(g[0]), - int(g[3]), int(g[4]), float(g[5])) - return _timegm(tt) - - # No, we need some messy parsing... - - # clean up - text = text.lstrip() - text = WEEKDAY_RE.sub("", text, 1) # Useless weekday - - # tz is time zone specifier string - day, mon, yr, hr, min, sec, tz = [None]*7 - - # loose regexp parse - m = LOOSE_HTTP_DATE_RE.search(text) - if m is not None: - day, mon, yr, hr, min, sec, tz = m.groups() - else: - return None # bad format - - return _str2time(day, mon, yr, hr, min, sec, tz) - -ISO_DATE_RE = re.compile( - """^ - (\d{4}) # year - [-\/]? - (\d\d?) # numerical month - [-\/]? - (\d\d?) # day - (?: - (?:\s+|[-:Tt]) # separator before clock - (\d\d?):?(\d\d) # hour:min - (?::?(\d\d(?:\.\d*)?))? # optional seconds (and fractional) - )? # optional clock - \s* - ([-+]?\d\d?:?(:?\d\d)? - |Z|z)? # timezone (Z is "zero meridian", i.e. GMT) - \s*$""", re.X | re. ASCII) -def iso2time(text): - """ - As for http2time, but parses the ISO 8601 formats: - - 1994-02-03 14:15:29 -0100 -- ISO 8601 format - 1994-02-03 14:15:29 -- zone is optional - 1994-02-03 -- only date - 1994-02-03T14:15:29 -- Use T as separator - 19940203T141529Z -- ISO 8601 compact format - 19940203 -- only date - - """ - # clean up - text = text.lstrip() - - # tz is time zone specifier string - day, mon, yr, hr, min, sec, tz = [None]*7 - - # loose regexp parse - m = ISO_DATE_RE.search(text) - if m is not None: - # XXX there's an extra bit of the timezone I'm ignoring here: is - # this the right thing to do? - yr, mon, day, hr, min, sec, tz, _ = m.groups() - else: - return None # bad format - - return _str2time(day, mon, yr, hr, min, sec, tz) - - -# Header parsing -# ----------------------------------------------------------------------------- - -def unmatched(match): - """Return unmatched part of re.Match object.""" - start, end = match.span(0) - return match.string[:start]+match.string[end:] - -HEADER_TOKEN_RE = re.compile(r"^\s*([^=\s;,]+)") -HEADER_QUOTED_VALUE_RE = re.compile(r"^\s*=\s*\"([^\"\\]*(?:\\.[^\"\\]*)*)\"") -HEADER_VALUE_RE = re.compile(r"^\s*=\s*([^\s;,]*)") -HEADER_ESCAPE_RE = re.compile(r"\\(.)") -def split_header_words(header_values): - r"""Parse header values into a list of lists containing key,value pairs. - - The function knows how to deal with ",", ";" and "=" as well as quoted - values after "=". A list of space separated tokens are parsed as if they - were separated by ";". - - If the header_values passed as argument contains multiple values, then they - are treated as if they were a single value separated by comma ",". - - This means that this function is useful for parsing header fields that - follow this syntax (BNF as from the HTTP/1.1 specification, but we relax - the requirement for tokens). - - headers = #header - header = (token | parameter) *( [";"] (token | parameter)) - - token = 1* - separators = "(" | ")" | "<" | ">" | "@" - | "," | ";" | ":" | "\" | <"> - | "/" | "[" | "]" | "?" | "=" - | "{" | "}" | SP | HT - - quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) - qdtext = > - quoted-pair = "\" CHAR - - parameter = attribute "=" value - attribute = token - value = token | quoted-string - - Each header is represented by a list of key/value pairs. The value for a - simple token (not part of a parameter) is None. Syntactically incorrect - headers will not necessarily be parsed as you would want. - - This is easier to describe with some examples: - - >>> split_header_words(['foo="bar"; port="80,81"; discard, bar=baz']) - [[('foo', 'bar'), ('port', '80,81'), ('discard', None)], [('bar', 'baz')]] - >>> split_header_words(['text/html; charset="iso-8859-1"']) - [[('text/html', None), ('charset', 'iso-8859-1')]] - >>> split_header_words([r'Basic realm="\"foo\bar\""']) - [[('Basic', None), ('realm', '"foobar"')]] - - """ - assert not isinstance(header_values, str) - result = [] - for text in header_values: - orig_text = text - pairs = [] - while text: - m = HEADER_TOKEN_RE.search(text) - if m: - text = unmatched(m) - name = m.group(1) - m = HEADER_QUOTED_VALUE_RE.search(text) - if m: # quoted value - text = unmatched(m) - value = m.group(1) - value = HEADER_ESCAPE_RE.sub(r"\1", value) - else: - m = HEADER_VALUE_RE.search(text) - if m: # unquoted value - text = unmatched(m) - value = m.group(1) - value = value.rstrip() - else: - # no value, a lone token - value = None - pairs.append((name, value)) - elif text.lstrip().startswith(","): - # concatenated headers, as per RFC 2616 section 4.2 - text = text.lstrip()[1:] - if pairs: result.append(pairs) - pairs = [] - else: - # skip junk - non_junk, nr_junk_chars = re.subn("^[=\s;]*", "", text) - assert nr_junk_chars > 0, ( - "split_header_words bug: '%s', '%s', %s" % - (orig_text, text, pairs)) - text = non_junk - if pairs: result.append(pairs) - return result - -HEADER_JOIN_ESCAPE_RE = re.compile(r"([\"\\])") -def join_header_words(lists): - """Do the inverse (almost) of the conversion done by split_header_words. - - Takes a list of lists of (key, value) pairs and produces a single header - value. Attribute values are quoted if needed. - - >>> join_header_words([[("text/plain", None), ("charset", "iso-8859/1")]]) - 'text/plain; charset="iso-8859/1"' - >>> join_header_words([[("text/plain", None)], [("charset", "iso-8859/1")]]) - 'text/plain, charset="iso-8859/1"' - - """ - headers = [] - for pairs in lists: - attr = [] - for k, v in pairs: - if v is not None: - if not re.search(r"^\w+$", v): - v = HEADER_JOIN_ESCAPE_RE.sub(r"\\\1", v) # escape " and \ - v = '"%s"' % v - k = "%s=%s" % (k, v) - attr.append(k) - if attr: headers.append("; ".join(attr)) - return ", ".join(headers) - -def strip_quotes(text): - if text.startswith('"'): - text = text[1:] - if text.endswith('"'): - text = text[:-1] - return text - -def parse_ns_headers(ns_headers): - """Ad-hoc parser for Netscape protocol cookie-attributes. - - The old Netscape cookie format for Set-Cookie can for instance contain - an unquoted "," in the expires field, so we have to use this ad-hoc - parser instead of split_header_words. - - XXX This may not make the best possible effort to parse all the crap - that Netscape Cookie headers contain. Ronald Tschalar's HTTPClient - parser is probably better, so could do worse than following that if - this ever gives any trouble. - - Currently, this is also used for parsing RFC 2109 cookies. - - """ - known_attrs = ("expires", "domain", "path", "secure", - # RFC 2109 attrs (may turn up in Netscape cookies, too) - "version", "port", "max-age") - - result = [] - for ns_header in ns_headers: - pairs = [] - version_set = False - for ii, param in enumerate(re.split(r";\s*", ns_header)): - param = param.rstrip() - if param == "": continue - if "=" not in param: - k, v = param, None - else: - k, v = re.split(r"\s*=\s*", param, 1) - k = k.lstrip() - if ii != 0: - lc = k.lower() - if lc in known_attrs: - k = lc - if k == "version": - # This is an RFC 2109 cookie. - v = strip_quotes(v) - version_set = True - if k == "expires": - # convert expires date to seconds since epoch - v = http2time(strip_quotes(v)) # None if invalid - pairs.append((k, v)) - - if pairs: - if not version_set: - pairs.append(("version", "0")) - result.append(pairs) - - return result - - -IPV4_RE = re.compile(r"\.\d+$", re.ASCII) -def is_HDN(text): - """Return True if text is a host domain name.""" - # XXX - # This may well be wrong. Which RFC is HDN defined in, if any (for - # the purposes of RFC 2965)? - # For the current implementation, what about IPv6? Remember to look - # at other uses of IPV4_RE also, if change this. - if IPV4_RE.search(text): - return False - if text == "": - return False - if text[0] == "." or text[-1] == ".": - return False - return True - -def domain_match(A, B): - """Return True if domain A domain-matches domain B, according to RFC 2965. - - A and B may be host domain names or IP addresses. - - RFC 2965, section 1: - - Host names can be specified either as an IP address or a HDN string. - Sometimes we compare one host name with another. (Such comparisons SHALL - be case-insensitive.) Host A's name domain-matches host B's if - - * their host name strings string-compare equal; or - - * A is a HDN string and has the form NB, where N is a non-empty - name string, B has the form .B', and B' is a HDN string. (So, - x.y.com domain-matches .Y.com but not Y.com.) - - Note that domain-match is not a commutative operation: a.b.c.com - domain-matches .c.com, but not the reverse. - - """ - # Note that, if A or B are IP addresses, the only relevant part of the - # definition of the domain-match algorithm is the direct string-compare. - A = A.lower() - B = B.lower() - if A == B: - return True - if not is_HDN(A): - return False - i = A.rfind(B) - if i == -1 or i == 0: - # A does not have form NB, or N is the empty string - return False - if not B.startswith("."): - return False - if not is_HDN(B[1:]): - return False - return True - -def liberal_is_HDN(text): - """Return True if text is a sort-of-like a host domain name. - - For accepting/blocking domains. - - """ - if IPV4_RE.search(text): - return False - return True - -def user_domain_match(A, B): - """For blocking/accepting domains. - - A and B may be host domain names or IP addresses. - - """ - A = A.lower() - B = B.lower() - if not (liberal_is_HDN(A) and liberal_is_HDN(B)): - if A == B: - # equal IP addresses - return True - return False - initial_dot = B.startswith(".") - if initial_dot and A.endswith(B): - return True - if not initial_dot and A == B: - return True - return False - -cut_port_re = re.compile(r":\d+$", re.ASCII) -def request_host(request): - """Return request-host, as defined by RFC 2965. - - Variation from RFC: returned value is lowercased, for convenient - comparison. - - """ - url = request.get_full_url() - host = urlparse(url)[1] - if host == "": - host = request.get_header("Host", "") - - # remove port, if present - host = cut_port_re.sub("", host, 1) - return host.lower() - -def eff_request_host(request): - """Return a tuple (request-host, effective request-host name). - - As defined by RFC 2965, except both are lowercased. - - """ - erhn = req_host = request_host(request) - if req_host.find(".") == -1 and not IPV4_RE.search(req_host): - erhn = req_host + ".local" - return req_host, erhn - -def request_path(request): - """Path component of request-URI, as defined by RFC 2965.""" - url = request.get_full_url() - parts = urlsplit(url) - path = escape_path(parts.path) - if not path.startswith("/"): - # fix bad RFC 2396 absoluteURI - path = "/" + path - return path - -def request_port(request): - host = request.host - i = host.find(':') - if i >= 0: - port = host[i+1:] - try: - int(port) - except ValueError: - _debug("nonnumeric port: '%s'", port) - return None - else: - port = DEFAULT_HTTP_PORT - return port - -# Characters in addition to A-Z, a-z, 0-9, '_', '.', and '-' that don't -# need to be escaped to form a valid HTTP URL (RFCs 2396 and 1738). -HTTP_PATH_SAFE = "%/;:@&=+$,!~*'()" -ESCAPED_CHAR_RE = re.compile(r"%([0-9a-fA-F][0-9a-fA-F])") -def uppercase_escaped_char(match): - return "%%%s" % match.group(1).upper() -def escape_path(path): - """Escape any invalid characters in HTTP URL, and uppercase all escapes.""" - # There's no knowing what character encoding was used to create URLs - # containing %-escapes, but since we have to pick one to escape invalid - # path characters, we pick UTF-8, as recommended in the HTML 4.0 - # specification: - # http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.2.1 - # And here, kind of: draft-fielding-uri-rfc2396bis-03 - # (And in draft IRI specification: draft-duerst-iri-05) - # (And here, for new URI schemes: RFC 2718) - path = quote(path, HTTP_PATH_SAFE) - path = ESCAPED_CHAR_RE.sub(uppercase_escaped_char, path) - return path - -def reach(h): - """Return reach of host h, as defined by RFC 2965, section 1. - - The reach R of a host name H is defined as follows: - - * If - - - H is the host domain name of a host; and, - - - H has the form A.B; and - - - A has no embedded (that is, interior) dots; and - - - B has at least one embedded dot, or B is the string "local". - then the reach of H is .B. - - * Otherwise, the reach of H is H. - - >>> reach("www.acme.com") - '.acme.com' - >>> reach("acme.com") - 'acme.com' - >>> reach("acme.local") - '.local' - - """ - i = h.find(".") - if i >= 0: - #a = h[:i] # this line is only here to show what a is - b = h[i+1:] - i = b.find(".") - if is_HDN(h) and (i >= 0 or b == "local"): - return "."+b - return h - -def is_third_party(request): - """ - - RFC 2965, section 3.3.6: - - An unverifiable transaction is to a third-party host if its request- - host U does not domain-match the reach R of the request-host O in the - origin transaction. - - """ - req_host = request_host(request) - if not domain_match(req_host, reach(request.get_origin_req_host())): - return True - else: - return False - - -class Cookie(object): - """HTTP Cookie. - - This class represents both Netscape and RFC 2965 cookies. - - This is deliberately a very simple class. It just holds attributes. It's - possible to construct Cookie instances that don't comply with the cookie - standards. CookieJar.make_cookies is the factory function for Cookie - objects -- it deals with cookie parsing, supplying defaults, and - normalising to the representation used in this class. CookiePolicy is - responsible for checking them to see whether they should be accepted from - and returned to the server. - - Note that the port may be present in the headers, but unspecified ("Port" - rather than"Port=80", for example); if this is the case, port is None. - - """ - - def __init__(self, version, name, value, - port, port_specified, - domain, domain_specified, domain_initial_dot, - path, path_specified, - secure, - expires, - discard, - comment, - comment_url, - rest, - rfc2109=False, - ): - - if version is not None: version = int(version) - if expires is not None: expires = int(expires) - if port is None and port_specified is True: - raise ValueError("if port is None, port_specified must be false") - - self.version = version - self.name = name - self.value = value - self.port = port - self.port_specified = port_specified - # normalise case, as per RFC 2965 section 3.3.3 - self.domain = domain.lower() - self.domain_specified = domain_specified - # Sigh. We need to know whether the domain given in the - # cookie-attribute had an initial dot, in order to follow RFC 2965 - # (as clarified in draft errata). Needed for the returned $Domain - # value. - self.domain_initial_dot = domain_initial_dot - self.path = path - self.path_specified = path_specified - self.secure = secure - self.expires = expires - self.discard = discard - self.comment = comment - self.comment_url = comment_url - self.rfc2109 = rfc2109 - - self._rest = copy.copy(rest) - - def has_nonstandard_attr(self, name): - return name in self._rest - def get_nonstandard_attr(self, name, default=None): - return self._rest.get(name, default) - def set_nonstandard_attr(self, name, value): - self._rest[name] = value - - def is_expired(self, now=None): - if now is None: now = time.time() - if (self.expires is not None) and (self.expires <= now): - return True - return False - - def __str__(self): - if self.port is None: p = "" - else: p = ":"+self.port - limit = self.domain + p + self.path - if self.value is not None: - namevalue = "%s=%s" % (self.name, self.value) - else: - namevalue = self.name - return "" % (namevalue, limit) - - @as_native_str() - def __repr__(self): - args = [] - for name in ("version", "name", "value", - "port", "port_specified", - "domain", "domain_specified", "domain_initial_dot", - "path", "path_specified", - "secure", "expires", "discard", "comment", "comment_url", - ): - attr = getattr(self, name) - ### Python-Future: - # Avoid u'...' prefixes for unicode strings: - if isinstance(attr, str): - attr = str(attr) - ### - args.append(str("%s=%s") % (name, repr(attr))) - args.append("rest=%s" % repr(self._rest)) - args.append("rfc2109=%s" % repr(self.rfc2109)) - return "Cookie(%s)" % ", ".join(args) - - -class CookiePolicy(object): - """Defines which cookies get accepted from and returned to server. - - May also modify cookies, though this is probably a bad idea. - - The subclass DefaultCookiePolicy defines the standard rules for Netscape - and RFC 2965 cookies -- override that if you want a customised policy. - - """ - def set_ok(self, cookie, request): - """Return true if (and only if) cookie should be accepted from server. - - Currently, pre-expired cookies never get this far -- the CookieJar - class deletes such cookies itself. - - """ - raise NotImplementedError() - - def return_ok(self, cookie, request): - """Return true if (and only if) cookie should be returned to server.""" - raise NotImplementedError() - - def domain_return_ok(self, domain, request): - """Return false if cookies should not be returned, given cookie domain. - """ - return True - - def path_return_ok(self, path, request): - """Return false if cookies should not be returned, given cookie path. - """ - return True - - -class DefaultCookiePolicy(CookiePolicy): - """Implements the standard rules for accepting and returning cookies.""" - - DomainStrictNoDots = 1 - DomainStrictNonDomain = 2 - DomainRFC2965Match = 4 - - DomainLiberal = 0 - DomainStrict = DomainStrictNoDots|DomainStrictNonDomain - - def __init__(self, - blocked_domains=None, allowed_domains=None, - netscape=True, rfc2965=False, - rfc2109_as_netscape=None, - hide_cookie2=False, - strict_domain=False, - strict_rfc2965_unverifiable=True, - strict_ns_unverifiable=False, - strict_ns_domain=DomainLiberal, - strict_ns_set_initial_dollar=False, - strict_ns_set_path=False, - ): - """Constructor arguments should be passed as keyword arguments only.""" - self.netscape = netscape - self.rfc2965 = rfc2965 - self.rfc2109_as_netscape = rfc2109_as_netscape - self.hide_cookie2 = hide_cookie2 - self.strict_domain = strict_domain - self.strict_rfc2965_unverifiable = strict_rfc2965_unverifiable - self.strict_ns_unverifiable = strict_ns_unverifiable - self.strict_ns_domain = strict_ns_domain - self.strict_ns_set_initial_dollar = strict_ns_set_initial_dollar - self.strict_ns_set_path = strict_ns_set_path - - if blocked_domains is not None: - self._blocked_domains = tuple(blocked_domains) - else: - self._blocked_domains = () - - if allowed_domains is not None: - allowed_domains = tuple(allowed_domains) - self._allowed_domains = allowed_domains - - def blocked_domains(self): - """Return the sequence of blocked domains (as a tuple).""" - return self._blocked_domains - def set_blocked_domains(self, blocked_domains): - """Set the sequence of blocked domains.""" - self._blocked_domains = tuple(blocked_domains) - - def is_blocked(self, domain): - for blocked_domain in self._blocked_domains: - if user_domain_match(domain, blocked_domain): - return True - return False - - def allowed_domains(self): - """Return None, or the sequence of allowed domains (as a tuple).""" - return self._allowed_domains - def set_allowed_domains(self, allowed_domains): - """Set the sequence of allowed domains, or None.""" - if allowed_domains is not None: - allowed_domains = tuple(allowed_domains) - self._allowed_domains = allowed_domains - - def is_not_allowed(self, domain): - if self._allowed_domains is None: - return False - for allowed_domain in self._allowed_domains: - if user_domain_match(domain, allowed_domain): - return False - return True - - def set_ok(self, cookie, request): - """ - If you override .set_ok(), be sure to call this method. If it returns - false, so should your subclass (assuming your subclass wants to be more - strict about which cookies to accept). - - """ - _debug(" - checking cookie %s=%s", cookie.name, cookie.value) - - assert cookie.name is not None - - for n in "version", "verifiability", "name", "path", "domain", "port": - fn_name = "set_ok_"+n - fn = getattr(self, fn_name) - if not fn(cookie, request): - return False - - return True - - def set_ok_version(self, cookie, request): - if cookie.version is None: - # Version is always set to 0 by parse_ns_headers if it's a Netscape - # cookie, so this must be an invalid RFC 2965 cookie. - _debug(" Set-Cookie2 without version attribute (%s=%s)", - cookie.name, cookie.value) - return False - if cookie.version > 0 and not self.rfc2965: - _debug(" RFC 2965 cookies are switched off") - return False - elif cookie.version == 0 and not self.netscape: - _debug(" Netscape cookies are switched off") - return False - return True - - def set_ok_verifiability(self, cookie, request): - if request.unverifiable and is_third_party(request): - if cookie.version > 0 and self.strict_rfc2965_unverifiable: - _debug(" third-party RFC 2965 cookie during " - "unverifiable transaction") - return False - elif cookie.version == 0 and self.strict_ns_unverifiable: - _debug(" third-party Netscape cookie during " - "unverifiable transaction") - return False - return True - - def set_ok_name(self, cookie, request): - # Try and stop servers setting V0 cookies designed to hack other - # servers that know both V0 and V1 protocols. - if (cookie.version == 0 and self.strict_ns_set_initial_dollar and - cookie.name.startswith("$")): - _debug(" illegal name (starts with '$'): '%s'", cookie.name) - return False - return True - - def set_ok_path(self, cookie, request): - if cookie.path_specified: - req_path = request_path(request) - if ((cookie.version > 0 or - (cookie.version == 0 and self.strict_ns_set_path)) and - not req_path.startswith(cookie.path)): - _debug(" path attribute %s is not a prefix of request " - "path %s", cookie.path, req_path) - return False - return True - - def set_ok_domain(self, cookie, request): - if self.is_blocked(cookie.domain): - _debug(" domain %s is in user block-list", cookie.domain) - return False - if self.is_not_allowed(cookie.domain): - _debug(" domain %s is not in user allow-list", cookie.domain) - return False - if cookie.domain_specified: - req_host, erhn = eff_request_host(request) - domain = cookie.domain - if self.strict_domain and (domain.count(".") >= 2): - # XXX This should probably be compared with the Konqueror - # (kcookiejar.cpp) and Mozilla implementations, but it's a - # losing battle. - i = domain.rfind(".") - j = domain.rfind(".", 0, i) - if j == 0: # domain like .foo.bar - tld = domain[i+1:] - sld = domain[j+1:i] - if sld.lower() in ("co", "ac", "com", "edu", "org", "net", - "gov", "mil", "int", "aero", "biz", "cat", "coop", - "info", "jobs", "mobi", "museum", "name", "pro", - "travel", "eu") and len(tld) == 2: - # domain like .co.uk - _debug(" country-code second level domain %s", domain) - return False - if domain.startswith("."): - undotted_domain = domain[1:] - else: - undotted_domain = domain - embedded_dots = (undotted_domain.find(".") >= 0) - if not embedded_dots and domain != ".local": - _debug(" non-local domain %s contains no embedded dot", - domain) - return False - if cookie.version == 0: - if (not erhn.endswith(domain) and - (not erhn.startswith(".") and - not ("."+erhn).endswith(domain))): - _debug(" effective request-host %s (even with added " - "initial dot) does not end with %s", - erhn, domain) - return False - if (cookie.version > 0 or - (self.strict_ns_domain & self.DomainRFC2965Match)): - if not domain_match(erhn, domain): - _debug(" effective request-host %s does not domain-match " - "%s", erhn, domain) - return False - if (cookie.version > 0 or - (self.strict_ns_domain & self.DomainStrictNoDots)): - host_prefix = req_host[:-len(domain)] - if (host_prefix.find(".") >= 0 and - not IPV4_RE.search(req_host)): - _debug(" host prefix %s for domain %s contains a dot", - host_prefix, domain) - return False - return True - - def set_ok_port(self, cookie, request): - if cookie.port_specified: - req_port = request_port(request) - if req_port is None: - req_port = "80" - else: - req_port = str(req_port) - for p in cookie.port.split(","): - try: - int(p) - except ValueError: - _debug(" bad port %s (not numeric)", p) - return False - if p == req_port: - break - else: - _debug(" request port (%s) not found in %s", - req_port, cookie.port) - return False - return True - - def return_ok(self, cookie, request): - """ - If you override .return_ok(), be sure to call this method. If it - returns false, so should your subclass (assuming your subclass wants to - be more strict about which cookies to return). - - """ - # Path has already been checked by .path_return_ok(), and domain - # blocking done by .domain_return_ok(). - _debug(" - checking cookie %s=%s", cookie.name, cookie.value) - - for n in "version", "verifiability", "secure", "expires", "port", "domain": - fn_name = "return_ok_"+n - fn = getattr(self, fn_name) - if not fn(cookie, request): - return False - return True - - def return_ok_version(self, cookie, request): - if cookie.version > 0 and not self.rfc2965: - _debug(" RFC 2965 cookies are switched off") - return False - elif cookie.version == 0 and not self.netscape: - _debug(" Netscape cookies are switched off") - return False - return True - - def return_ok_verifiability(self, cookie, request): - if request.unverifiable and is_third_party(request): - if cookie.version > 0 and self.strict_rfc2965_unverifiable: - _debug(" third-party RFC 2965 cookie during unverifiable " - "transaction") - return False - elif cookie.version == 0 and self.strict_ns_unverifiable: - _debug(" third-party Netscape cookie during unverifiable " - "transaction") - return False - return True - - def return_ok_secure(self, cookie, request): - if cookie.secure and request.type != "https": - _debug(" secure cookie with non-secure request") - return False - return True - - def return_ok_expires(self, cookie, request): - if cookie.is_expired(self._now): - _debug(" cookie expired") - return False - return True - - def return_ok_port(self, cookie, request): - if cookie.port: - req_port = request_port(request) - if req_port is None: - req_port = "80" - for p in cookie.port.split(","): - if p == req_port: - break - else: - _debug(" request port %s does not match cookie port %s", - req_port, cookie.port) - return False - return True - - def return_ok_domain(self, cookie, request): - req_host, erhn = eff_request_host(request) - domain = cookie.domain - - # strict check of non-domain cookies: Mozilla does this, MSIE5 doesn't - if (cookie.version == 0 and - (self.strict_ns_domain & self.DomainStrictNonDomain) and - not cookie.domain_specified and domain != erhn): - _debug(" cookie with unspecified domain does not string-compare " - "equal to request domain") - return False - - if cookie.version > 0 and not domain_match(erhn, domain): - _debug(" effective request-host name %s does not domain-match " - "RFC 2965 cookie domain %s", erhn, domain) - return False - if cookie.version == 0 and not ("."+erhn).endswith(domain): - _debug(" request-host %s does not match Netscape cookie domain " - "%s", req_host, domain) - return False - return True - - def domain_return_ok(self, domain, request): - # Liberal check of. This is here as an optimization to avoid - # having to load lots of MSIE cookie files unless necessary. - req_host, erhn = eff_request_host(request) - if not req_host.startswith("."): - req_host = "."+req_host - if not erhn.startswith("."): - erhn = "."+erhn - if not (req_host.endswith(domain) or erhn.endswith(domain)): - #_debug(" request domain %s does not match cookie domain %s", - # req_host, domain) - return False - - if self.is_blocked(domain): - _debug(" domain %s is in user block-list", domain) - return False - if self.is_not_allowed(domain): - _debug(" domain %s is not in user allow-list", domain) - return False - - return True - - def path_return_ok(self, path, request): - _debug("- checking cookie path=%s", path) - req_path = request_path(request) - if not req_path.startswith(path): - _debug(" %s does not path-match %s", req_path, path) - return False - return True - - -def vals_sorted_by_key(adict): - keys = sorted(adict.keys()) - return map(adict.get, keys) - -def deepvalues(mapping): - """Iterates over nested mapping, depth-first, in sorted order by key.""" - values = vals_sorted_by_key(mapping) - for obj in values: - mapping = False - try: - obj.items - except AttributeError: - pass - else: - mapping = True - for subobj in deepvalues(obj): - yield subobj - if not mapping: - yield obj - - -# Used as second parameter to dict.get() method, to distinguish absent -# dict key from one with a None value. -class Absent(object): pass - -class CookieJar(object): - """Collection of HTTP cookies. - - You may not need to know about this class: try - urllib.request.build_opener(HTTPCookieProcessor).open(url). - """ - - non_word_re = re.compile(r"\W") - quote_re = re.compile(r"([\"\\])") - strict_domain_re = re.compile(r"\.?[^.]*") - domain_re = re.compile(r"[^.]*") - dots_re = re.compile(r"^\.+") - - magic_re = re.compile(r"^\#LWP-Cookies-(\d+\.\d+)", re.ASCII) - - def __init__(self, policy=None): - if policy is None: - policy = DefaultCookiePolicy() - self._policy = policy - - self._cookies_lock = _threading.RLock() - self._cookies = {} - - def set_policy(self, policy): - self._policy = policy - - def _cookies_for_domain(self, domain, request): - cookies = [] - if not self._policy.domain_return_ok(domain, request): - return [] - _debug("Checking %s for cookies to return", domain) - cookies_by_path = self._cookies[domain] - for path in cookies_by_path.keys(): - if not self._policy.path_return_ok(path, request): - continue - cookies_by_name = cookies_by_path[path] - for cookie in cookies_by_name.values(): - if not self._policy.return_ok(cookie, request): - _debug(" not returning cookie") - continue - _debug(" it's a match") - cookies.append(cookie) - return cookies - - def _cookies_for_request(self, request): - """Return a list of cookies to be returned to server.""" - cookies = [] - for domain in self._cookies.keys(): - cookies.extend(self._cookies_for_domain(domain, request)) - return cookies - - def _cookie_attrs(self, cookies): - """Return a list of cookie-attributes to be returned to server. - - like ['foo="bar"; $Path="/"', ...] - - The $Version attribute is also added when appropriate (currently only - once per request). - - """ - # add cookies in order of most specific (ie. longest) path first - cookies.sort(key=lambda a: len(a.path), reverse=True) - - version_set = False - - attrs = [] - for cookie in cookies: - # set version of Cookie header - # XXX - # What should it be if multiple matching Set-Cookie headers have - # different versions themselves? - # Answer: there is no answer; was supposed to be settled by - # RFC 2965 errata, but that may never appear... - version = cookie.version - if not version_set: - version_set = True - if version > 0: - attrs.append("$Version=%s" % version) - - # quote cookie value if necessary - # (not for Netscape protocol, which already has any quotes - # intact, due to the poorly-specified Netscape Cookie: syntax) - if ((cookie.value is not None) and - self.non_word_re.search(cookie.value) and version > 0): - value = self.quote_re.sub(r"\\\1", cookie.value) - else: - value = cookie.value - - # add cookie-attributes to be returned in Cookie header - if cookie.value is None: - attrs.append(cookie.name) - else: - attrs.append("%s=%s" % (cookie.name, value)) - if version > 0: - if cookie.path_specified: - attrs.append('$Path="%s"' % cookie.path) - if cookie.domain.startswith("."): - domain = cookie.domain - if (not cookie.domain_initial_dot and - domain.startswith(".")): - domain = domain[1:] - attrs.append('$Domain="%s"' % domain) - if cookie.port is not None: - p = "$Port" - if cookie.port_specified: - p = p + ('="%s"' % cookie.port) - attrs.append(p) - - return attrs - - def add_cookie_header(self, request): - """Add correct Cookie: header to request (urllib.request.Request object). - - The Cookie2 header is also added unless policy.hide_cookie2 is true. - - """ - _debug("add_cookie_header") - self._cookies_lock.acquire() - try: - - self._policy._now = self._now = int(time.time()) - - cookies = self._cookies_for_request(request) - - attrs = self._cookie_attrs(cookies) - if attrs: - if not request.has_header("Cookie"): - request.add_unredirected_header( - "Cookie", "; ".join(attrs)) - - # if necessary, advertise that we know RFC 2965 - if (self._policy.rfc2965 and not self._policy.hide_cookie2 and - not request.has_header("Cookie2")): - for cookie in cookies: - if cookie.version != 1: - request.add_unredirected_header("Cookie2", '$Version="1"') - break - - finally: - self._cookies_lock.release() - - self.clear_expired_cookies() - - def _normalized_cookie_tuples(self, attrs_set): - """Return list of tuples containing normalised cookie information. - - attrs_set is the list of lists of key,value pairs extracted from - the Set-Cookie or Set-Cookie2 headers. - - Tuples are name, value, standard, rest, where name and value are the - cookie name and value, standard is a dictionary containing the standard - cookie-attributes (discard, secure, version, expires or max-age, - domain, path and port) and rest is a dictionary containing the rest of - the cookie-attributes. - - """ - cookie_tuples = [] - - boolean_attrs = "discard", "secure" - value_attrs = ("version", - "expires", "max-age", - "domain", "path", "port", - "comment", "commenturl") - - for cookie_attrs in attrs_set: - name, value = cookie_attrs[0] - - # Build dictionary of standard cookie-attributes (standard) and - # dictionary of other cookie-attributes (rest). - - # Note: expiry time is normalised to seconds since epoch. V0 - # cookies should have the Expires cookie-attribute, and V1 cookies - # should have Max-Age, but since V1 includes RFC 2109 cookies (and - # since V0 cookies may be a mish-mash of Netscape and RFC 2109), we - # accept either (but prefer Max-Age). - max_age_set = False - - bad_cookie = False - - standard = {} - rest = {} - for k, v in cookie_attrs[1:]: - lc = k.lower() - # don't lose case distinction for unknown fields - if lc in value_attrs or lc in boolean_attrs: - k = lc - if k in boolean_attrs and v is None: - # boolean cookie-attribute is present, but has no value - # (like "discard", rather than "port=80") - v = True - if k in standard: - # only first value is significant - continue - if k == "domain": - if v is None: - _debug(" missing value for domain attribute") - bad_cookie = True - break - # RFC 2965 section 3.3.3 - v = v.lower() - if k == "expires": - if max_age_set: - # Prefer max-age to expires (like Mozilla) - continue - if v is None: - _debug(" missing or invalid value for expires " - "attribute: treating as session cookie") - continue - if k == "max-age": - max_age_set = True - try: - v = int(v) - except ValueError: - _debug(" missing or invalid (non-numeric) value for " - "max-age attribute") - bad_cookie = True - break - # convert RFC 2965 Max-Age to seconds since epoch - # XXX Strictly you're supposed to follow RFC 2616 - # age-calculation rules. Remember that zero Max-Age is a - # is a request to discard (old and new) cookie, though. - k = "expires" - v = self._now + v - if (k in value_attrs) or (k in boolean_attrs): - if (v is None and - k not in ("port", "comment", "commenturl")): - _debug(" missing value for %s attribute" % k) - bad_cookie = True - break - standard[k] = v - else: - rest[k] = v - - if bad_cookie: - continue - - cookie_tuples.append((name, value, standard, rest)) - - return cookie_tuples - - def _cookie_from_cookie_tuple(self, tup, request): - # standard is dict of standard cookie-attributes, rest is dict of the - # rest of them - name, value, standard, rest = tup - - domain = standard.get("domain", Absent) - path = standard.get("path", Absent) - port = standard.get("port", Absent) - expires = standard.get("expires", Absent) - - # set the easy defaults - version = standard.get("version", None) - if version is not None: - try: - version = int(version) - except ValueError: - return None # invalid version, ignore cookie - secure = standard.get("secure", False) - # (discard is also set if expires is Absent) - discard = standard.get("discard", False) - comment = standard.get("comment", None) - comment_url = standard.get("commenturl", None) - - # set default path - if path is not Absent and path != "": - path_specified = True - path = escape_path(path) - else: - path_specified = False - path = request_path(request) - i = path.rfind("/") - if i != -1: - if version == 0: - # Netscape spec parts company from reality here - path = path[:i] - else: - path = path[:i+1] - if len(path) == 0: path = "/" - - # set default domain - domain_specified = domain is not Absent - # but first we have to remember whether it starts with a dot - domain_initial_dot = False - if domain_specified: - domain_initial_dot = bool(domain.startswith(".")) - if domain is Absent: - req_host, erhn = eff_request_host(request) - domain = erhn - elif not domain.startswith("."): - domain = "."+domain - - # set default port - port_specified = False - if port is not Absent: - if port is None: - # Port attr present, but has no value: default to request port. - # Cookie should then only be sent back on that port. - port = request_port(request) - else: - port_specified = True - port = re.sub(r"\s+", "", port) - else: - # No port attr present. Cookie can be sent back on any port. - port = None - - # set default expires and discard - if expires is Absent: - expires = None - discard = True - elif expires <= self._now: - # Expiry date in past is request to delete cookie. This can't be - # in DefaultCookiePolicy, because can't delete cookies there. - try: - self.clear(domain, path, name) - except KeyError: - pass - _debug("Expiring cookie, domain='%s', path='%s', name='%s'", - domain, path, name) - return None - - return Cookie(version, - name, value, - port, port_specified, - domain, domain_specified, domain_initial_dot, - path, path_specified, - secure, - expires, - discard, - comment, - comment_url, - rest) - - def _cookies_from_attrs_set(self, attrs_set, request): - cookie_tuples = self._normalized_cookie_tuples(attrs_set) - - cookies = [] - for tup in cookie_tuples: - cookie = self._cookie_from_cookie_tuple(tup, request) - if cookie: cookies.append(cookie) - return cookies - - def _process_rfc2109_cookies(self, cookies): - rfc2109_as_ns = getattr(self._policy, 'rfc2109_as_netscape', None) - if rfc2109_as_ns is None: - rfc2109_as_ns = not self._policy.rfc2965 - for cookie in cookies: - if cookie.version == 1: - cookie.rfc2109 = True - if rfc2109_as_ns: - # treat 2109 cookies as Netscape cookies rather than - # as RFC2965 cookies - cookie.version = 0 - - def make_cookies(self, response, request): - """Return sequence of Cookie objects extracted from response object.""" - # get cookie-attributes for RFC 2965 and Netscape protocols - headers = response.info() - rfc2965_hdrs = headers.get_all("Set-Cookie2", []) - ns_hdrs = headers.get_all("Set-Cookie", []) - - rfc2965 = self._policy.rfc2965 - netscape = self._policy.netscape - - if ((not rfc2965_hdrs and not ns_hdrs) or - (not ns_hdrs and not rfc2965) or - (not rfc2965_hdrs and not netscape) or - (not netscape and not rfc2965)): - return [] # no relevant cookie headers: quick exit - - try: - cookies = self._cookies_from_attrs_set( - split_header_words(rfc2965_hdrs), request) - except Exception: - _warn_unhandled_exception() - cookies = [] - - if ns_hdrs and netscape: - try: - # RFC 2109 and Netscape cookies - ns_cookies = self._cookies_from_attrs_set( - parse_ns_headers(ns_hdrs), request) - except Exception: - _warn_unhandled_exception() - ns_cookies = [] - self._process_rfc2109_cookies(ns_cookies) - - # Look for Netscape cookies (from Set-Cookie headers) that match - # corresponding RFC 2965 cookies (from Set-Cookie2 headers). - # For each match, keep the RFC 2965 cookie and ignore the Netscape - # cookie (RFC 2965 section 9.1). Actually, RFC 2109 cookies are - # bundled in with the Netscape cookies for this purpose, which is - # reasonable behaviour. - if rfc2965: - lookup = {} - for cookie in cookies: - lookup[(cookie.domain, cookie.path, cookie.name)] = None - - def no_matching_rfc2965(ns_cookie, lookup=lookup): - key = ns_cookie.domain, ns_cookie.path, ns_cookie.name - return key not in lookup - ns_cookies = filter(no_matching_rfc2965, ns_cookies) - - if ns_cookies: - cookies.extend(ns_cookies) - - return cookies - - def set_cookie_if_ok(self, cookie, request): - """Set a cookie if policy says it's OK to do so.""" - self._cookies_lock.acquire() - try: - self._policy._now = self._now = int(time.time()) - - if self._policy.set_ok(cookie, request): - self.set_cookie(cookie) - - - finally: - self._cookies_lock.release() - - def set_cookie(self, cookie): - """Set a cookie, without checking whether or not it should be set.""" - c = self._cookies - self._cookies_lock.acquire() - try: - if cookie.domain not in c: c[cookie.domain] = {} - c2 = c[cookie.domain] - if cookie.path not in c2: c2[cookie.path] = {} - c3 = c2[cookie.path] - c3[cookie.name] = cookie - finally: - self._cookies_lock.release() - - def extract_cookies(self, response, request): - """Extract cookies from response, where allowable given the request.""" - _debug("extract_cookies: %s", response.info()) - self._cookies_lock.acquire() - try: - self._policy._now = self._now = int(time.time()) - - for cookie in self.make_cookies(response, request): - if self._policy.set_ok(cookie, request): - _debug(" setting cookie: %s", cookie) - self.set_cookie(cookie) - finally: - self._cookies_lock.release() - - def clear(self, domain=None, path=None, name=None): - """Clear some cookies. - - Invoking this method without arguments will clear all cookies. If - given a single argument, only cookies belonging to that domain will be - removed. If given two arguments, cookies belonging to the specified - path within that domain are removed. If given three arguments, then - the cookie with the specified name, path and domain is removed. - - Raises KeyError if no matching cookie exists. - - """ - if name is not None: - if (domain is None) or (path is None): - raise ValueError( - "domain and path must be given to remove a cookie by name") - del self._cookies[domain][path][name] - elif path is not None: - if domain is None: - raise ValueError( - "domain must be given to remove cookies by path") - del self._cookies[domain][path] - elif domain is not None: - del self._cookies[domain] - else: - self._cookies = {} - - def clear_session_cookies(self): - """Discard all session cookies. - - Note that the .save() method won't save session cookies anyway, unless - you ask otherwise by passing a true ignore_discard argument. - - """ - self._cookies_lock.acquire() - try: - for cookie in self: - if cookie.discard: - self.clear(cookie.domain, cookie.path, cookie.name) - finally: - self._cookies_lock.release() - - def clear_expired_cookies(self): - """Discard all expired cookies. - - You probably don't need to call this method: expired cookies are never - sent back to the server (provided you're using DefaultCookiePolicy), - this method is called by CookieJar itself every so often, and the - .save() method won't save expired cookies anyway (unless you ask - otherwise by passing a true ignore_expires argument). - - """ - self._cookies_lock.acquire() - try: - now = time.time() - for cookie in self: - if cookie.is_expired(now): - self.clear(cookie.domain, cookie.path, cookie.name) - finally: - self._cookies_lock.release() - - def __iter__(self): - return deepvalues(self._cookies) - - def __len__(self): - """Return number of contained cookies.""" - i = 0 - for cookie in self: i = i + 1 - return i - - @as_native_str() - def __repr__(self): - r = [] - for cookie in self: r.append(repr(cookie)) - return "<%s[%s]>" % (self.__class__, ", ".join(r)) - - def __str__(self): - r = [] - for cookie in self: r.append(str(cookie)) - return "<%s[%s]>" % (self.__class__, ", ".join(r)) - - -# derives from IOError for backwards-compatibility with Python 2.4.0 -class LoadError(IOError): pass - -class FileCookieJar(CookieJar): - """CookieJar that can be loaded from and saved to a file.""" - - def __init__(self, filename=None, delayload=False, policy=None): - """ - Cookies are NOT loaded from the named file until either the .load() or - .revert() method is called. - - """ - CookieJar.__init__(self, policy) - if filename is not None: - try: - filename+"" - except: - raise ValueError("filename must be string-like") - self.filename = filename - self.delayload = bool(delayload) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - """Save cookies to a file.""" - raise NotImplementedError() - - def load(self, filename=None, ignore_discard=False, ignore_expires=False): - """Load cookies from a file.""" - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename) - try: - self._really_load(f, filename, ignore_discard, ignore_expires) - finally: - f.close() - - def revert(self, filename=None, - ignore_discard=False, ignore_expires=False): - """Clear all cookies and reload cookies from a saved file. - - Raises LoadError (or IOError) if reversion is not successful; the - object's state will not be altered if this happens. - - """ - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - self._cookies_lock.acquire() - try: - - old_state = copy.deepcopy(self._cookies) - self._cookies = {} - try: - self.load(filename, ignore_discard, ignore_expires) - except (LoadError, IOError): - self._cookies = old_state - raise - - finally: - self._cookies_lock.release() - - -def lwp_cookie_str(cookie): - """Return string representation of Cookie in an the LWP cookie file format. - - Actually, the format is extended a bit -- see module docstring. - - """ - h = [(cookie.name, cookie.value), - ("path", cookie.path), - ("domain", cookie.domain)] - if cookie.port is not None: h.append(("port", cookie.port)) - if cookie.path_specified: h.append(("path_spec", None)) - if cookie.port_specified: h.append(("port_spec", None)) - if cookie.domain_initial_dot: h.append(("domain_dot", None)) - if cookie.secure: h.append(("secure", None)) - if cookie.expires: h.append(("expires", - time2isoz(float(cookie.expires)))) - if cookie.discard: h.append(("discard", None)) - if cookie.comment: h.append(("comment", cookie.comment)) - if cookie.comment_url: h.append(("commenturl", cookie.comment_url)) - - keys = sorted(cookie._rest.keys()) - for k in keys: - h.append((k, str(cookie._rest[k]))) - - h.append(("version", str(cookie.version))) - - return join_header_words([h]) - -class LWPCookieJar(FileCookieJar): - """ - The LWPCookieJar saves a sequence of "Set-Cookie3" lines. - "Set-Cookie3" is the format used by the libwww-perl libary, not known - to be compatible with any browser, but which is easy to read and - doesn't lose information about RFC 2965 cookies. - - Additional methods - - as_lwp_str(ignore_discard=True, ignore_expired=True) - - """ - - def as_lwp_str(self, ignore_discard=True, ignore_expires=True): - """Return cookies as a string of "\\n"-separated "Set-Cookie3" headers. - - ignore_discard and ignore_expires: see docstring for FileCookieJar.save - - """ - now = time.time() - r = [] - for cookie in self: - if not ignore_discard and cookie.discard: - continue - if not ignore_expires and cookie.is_expired(now): - continue - r.append("Set-Cookie3: %s" % lwp_cookie_str(cookie)) - return "\n".join(r+[""]) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename, "w") - try: - # There really isn't an LWP Cookies 2.0 format, but this indicates - # that there is extra information in here (domain_dot and - # port_spec) while still being compatible with libwww-perl, I hope. - f.write("#LWP-Cookies-2.0\n") - f.write(self.as_lwp_str(ignore_discard, ignore_expires)) - finally: - f.close() - - def _really_load(self, f, filename, ignore_discard, ignore_expires): - magic = f.readline() - if not self.magic_re.search(magic): - msg = ("%r does not look like a Set-Cookie3 (LWP) format " - "file" % filename) - raise LoadError(msg) - - now = time.time() - - header = "Set-Cookie3:" - boolean_attrs = ("port_spec", "path_spec", "domain_dot", - "secure", "discard") - value_attrs = ("version", - "port", "path", "domain", - "expires", - "comment", "commenturl") - - try: - while 1: - line = f.readline() - if line == "": break - if not line.startswith(header): - continue - line = line[len(header):].strip() - - for data in split_header_words([line]): - name, value = data[0] - standard = {} - rest = {} - for k in boolean_attrs: - standard[k] = False - for k, v in data[1:]: - if k is not None: - lc = k.lower() - else: - lc = None - # don't lose case distinction for unknown fields - if (lc in value_attrs) or (lc in boolean_attrs): - k = lc - if k in boolean_attrs: - if v is None: v = True - standard[k] = v - elif k in value_attrs: - standard[k] = v - else: - rest[k] = v - - h = standard.get - expires = h("expires") - discard = h("discard") - if expires is not None: - expires = iso2time(expires) - if expires is None: - discard = True - domain = h("domain") - domain_specified = domain.startswith(".") - c = Cookie(h("version"), name, value, - h("port"), h("port_spec"), - domain, domain_specified, h("domain_dot"), - h("path"), h("path_spec"), - h("secure"), - expires, - discard, - h("comment"), - h("commenturl"), - rest) - if not ignore_discard and c.discard: - continue - if not ignore_expires and c.is_expired(now): - continue - self.set_cookie(c) - - except IOError: - raise - except Exception: - _warn_unhandled_exception() - raise LoadError("invalid Set-Cookie3 format file %r: %r" % - (filename, line)) - - -class MozillaCookieJar(FileCookieJar): - """ - - WARNING: you may want to backup your browser's cookies file if you use - this class to save cookies. I *think* it works, but there have been - bugs in the past! - - This class differs from CookieJar only in the format it uses to save and - load cookies to and from a file. This class uses the Mozilla/Netscape - `cookies.txt' format. lynx uses this file format, too. - - Don't expect cookies saved while the browser is running to be noticed by - the browser (in fact, Mozilla on unix will overwrite your saved cookies if - you change them on disk while it's running; on Windows, you probably can't - save at all while the browser is running). - - Note that the Mozilla/Netscape format will downgrade RFC2965 cookies to - Netscape cookies on saving. - - In particular, the cookie version and port number information is lost, - together with information about whether or not Path, Port and Discard were - specified by the Set-Cookie2 (or Set-Cookie) header, and whether or not the - domain as set in the HTTP header started with a dot (yes, I'm aware some - domains in Netscape files start with a dot and some don't -- trust me, you - really don't want to know any more about this). - - Note that though Mozilla and Netscape use the same format, they use - slightly different headers. The class saves cookies using the Netscape - header by default (Mozilla can cope with that). - - """ - magic_re = re.compile("#( Netscape)? HTTP Cookie File") - header = """\ -# Netscape HTTP Cookie File -# http://www.netscape.com/newsref/std/cookie_spec.html -# This is a generated file! Do not edit. - -""" - - def _really_load(self, f, filename, ignore_discard, ignore_expires): - now = time.time() - - magic = f.readline() - if not self.magic_re.search(magic): - f.close() - raise LoadError( - "%r does not look like a Netscape format cookies file" % - filename) - - try: - while 1: - line = f.readline() - if line == "": break - - # last field may be absent, so keep any trailing tab - if line.endswith("\n"): line = line[:-1] - - # skip comments and blank lines XXX what is $ for? - if (line.strip().startswith(("#", "$")) or - line.strip() == ""): - continue - - domain, domain_specified, path, secure, expires, name, value = \ - line.split("\t") - secure = (secure == "TRUE") - domain_specified = (domain_specified == "TRUE") - if name == "": - # cookies.txt regards 'Set-Cookie: foo' as a cookie - # with no name, whereas http.cookiejar regards it as a - # cookie with no value. - name = value - value = None - - initial_dot = domain.startswith(".") - assert domain_specified == initial_dot - - discard = False - if expires == "": - expires = None - discard = True - - # assume path_specified is false - c = Cookie(0, name, value, - None, False, - domain, domain_specified, initial_dot, - path, False, - secure, - expires, - discard, - None, - None, - {}) - if not ignore_discard and c.discard: - continue - if not ignore_expires and c.is_expired(now): - continue - self.set_cookie(c) - - except IOError: - raise - except Exception: - _warn_unhandled_exception() - raise LoadError("invalid Netscape format cookies file %r: %r" % - (filename, line)) - - def save(self, filename=None, ignore_discard=False, ignore_expires=False): - if filename is None: - if self.filename is not None: filename = self.filename - else: raise ValueError(MISSING_FILENAME_TEXT) - - f = open(filename, "w") - try: - f.write(self.header) - now = time.time() - for cookie in self: - if not ignore_discard and cookie.discard: - continue - if not ignore_expires and cookie.is_expired(now): - continue - if cookie.secure: secure = "TRUE" - else: secure = "FALSE" - if cookie.domain.startswith("."): initial_dot = "TRUE" - else: initial_dot = "FALSE" - if cookie.expires is not None: - expires = str(cookie.expires) - else: - expires = "" - if cookie.value is None: - # cookies.txt regards 'Set-Cookie: foo' as a cookie - # with no name, whereas http.cookiejar regards it as a - # cookie with no value. - name = "" - value = cookie.name - else: - name = cookie.name - value = cookie.value - f.write( - "\t".join([cookie.domain, initial_dot, cookie.path, - secure, expires, name, value])+ - "\n") - finally: - f.close() diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/cookies.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/cookies.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/cookies.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/cookies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,597 +0,0 @@ -#### -# Copyright 2000 by Timothy O'Malley -# -# All Rights Reserved -# -# Permission to use, copy, modify, and distribute this software -# and its documentation for any purpose and without fee is hereby -# granted, provided that the above copyright notice appear in all -# copies and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Timothy O'Malley not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# Timothy O'Malley DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS -# SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY -# AND FITNESS, IN NO EVENT SHALL Timothy O'Malley BE LIABLE FOR -# ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -# PERFORMANCE OF THIS SOFTWARE. -# -#### -# -# Id: Cookie.py,v 2.29 2000/08/23 05:28:49 timo Exp -# by Timothy O'Malley -# -# Cookie.py is a Python module for the handling of HTTP -# cookies as a Python dictionary. See RFC 2109 for more -# information on cookies. -# -# The original idea to treat Cookies as a dictionary came from -# Dave Mitchell (davem@magnet.com) in 1995, when he released the -# first version of nscookie.py. -# -#### - -r""" -http.cookies module ported to python-future from Py3.3 - -Here's a sample session to show how to use this module. -At the moment, this is the only documentation. - -The Basics ----------- - -Importing is easy... - - >>> from http import cookies - -Most of the time you start by creating a cookie. - - >>> C = cookies.SimpleCookie() - -Once you've created your Cookie, you can add values just as if it were -a dictionary. - - >>> C = cookies.SimpleCookie() - >>> C["fig"] = "newton" - >>> C["sugar"] = "wafer" - >>> C.output() - 'Set-Cookie: fig=newton\r\nSet-Cookie: sugar=wafer' - -Notice that the printable representation of a Cookie is the -appropriate format for a Set-Cookie: header. This is the -default behavior. You can change the header and printed -attributes by using the .output() function - - >>> C = cookies.SimpleCookie() - >>> C["rocky"] = "road" - >>> C["rocky"]["path"] = "/cookie" - >>> print(C.output(header="Cookie:")) - Cookie: rocky=road; Path=/cookie - >>> print(C.output(attrs=[], header="Cookie:")) - Cookie: rocky=road - -The load() method of a Cookie extracts cookies from a string. In a -CGI script, you would use this method to extract the cookies from the -HTTP_COOKIE environment variable. - - >>> C = cookies.SimpleCookie() - >>> C.load("chips=ahoy; vienna=finger") - >>> C.output() - 'Set-Cookie: chips=ahoy\r\nSet-Cookie: vienna=finger' - -The load() method is darn-tootin smart about identifying cookies -within a string. Escaped quotation marks, nested semicolons, and other -such trickeries do not confuse it. - - >>> C = cookies.SimpleCookie() - >>> C.load('keebler="E=everybody; L=\\"Loves\\"; fudge=\\012;";') - >>> print(C) - Set-Cookie: keebler="E=everybody; L=\"Loves\"; fudge=\012;" - -Each element of the Cookie also supports all of the RFC 2109 -Cookie attributes. Here's an example which sets the Path -attribute. - - >>> C = cookies.SimpleCookie() - >>> C["oreo"] = "doublestuff" - >>> C["oreo"]["path"] = "/" - >>> print(C) - Set-Cookie: oreo=doublestuff; Path=/ - -Each dictionary element has a 'value' attribute, which gives you -back the value associated with the key. - - >>> C = cookies.SimpleCookie() - >>> C["twix"] = "none for you" - >>> C["twix"].value - 'none for you' - -The SimpleCookie expects that all values should be standard strings. -Just to be sure, SimpleCookie invokes the str() builtin to convert -the value to a string, when the values are set dictionary-style. - - >>> C = cookies.SimpleCookie() - >>> C["number"] = 7 - >>> C["string"] = "seven" - >>> C["number"].value - '7' - >>> C["string"].value - 'seven' - >>> C.output() - 'Set-Cookie: number=7\r\nSet-Cookie: string=seven' - -Finis. -""" -from __future__ import unicode_literals -from __future__ import print_function -from __future__ import division -from __future__ import absolute_import -from future.builtins import chr, dict, int, str -from future.utils import PY2, as_native_str - -# -# Import our required modules -# -import re -re.ASCII = 0 # for py2 compatibility -import string - -__all__ = ["CookieError", "BaseCookie", "SimpleCookie"] - -_nulljoin = ''.join -_semispacejoin = '; '.join -_spacejoin = ' '.join - -# -# Define an exception visible to External modules -# -class CookieError(Exception): - pass - - -# These quoting routines conform to the RFC2109 specification, which in -# turn references the character definitions from RFC2068. They provide -# a two-way quoting algorithm. Any non-text character is translated -# into a 4 character sequence: a forward-slash followed by the -# three-digit octal equivalent of the character. Any '\' or '"' is -# quoted with a preceeding '\' slash. -# -# These are taken from RFC2068 and RFC2109. -# _LegalChars is the list of chars which don't require "'s -# _Translator hash-table for fast quoting -# -_LegalChars = string.ascii_letters + string.digits + "!#$%&'*+-.^_`|~:" -_Translator = { - '\000' : '\\000', '\001' : '\\001', '\002' : '\\002', - '\003' : '\\003', '\004' : '\\004', '\005' : '\\005', - '\006' : '\\006', '\007' : '\\007', '\010' : '\\010', - '\011' : '\\011', '\012' : '\\012', '\013' : '\\013', - '\014' : '\\014', '\015' : '\\015', '\016' : '\\016', - '\017' : '\\017', '\020' : '\\020', '\021' : '\\021', - '\022' : '\\022', '\023' : '\\023', '\024' : '\\024', - '\025' : '\\025', '\026' : '\\026', '\027' : '\\027', - '\030' : '\\030', '\031' : '\\031', '\032' : '\\032', - '\033' : '\\033', '\034' : '\\034', '\035' : '\\035', - '\036' : '\\036', '\037' : '\\037', - - # Because of the way browsers really handle cookies (as opposed - # to what the RFC says) we also encode , and ; - - ',' : '\\054', ';' : '\\073', - - '"' : '\\"', '\\' : '\\\\', - - '\177' : '\\177', '\200' : '\\200', '\201' : '\\201', - '\202' : '\\202', '\203' : '\\203', '\204' : '\\204', - '\205' : '\\205', '\206' : '\\206', '\207' : '\\207', - '\210' : '\\210', '\211' : '\\211', '\212' : '\\212', - '\213' : '\\213', '\214' : '\\214', '\215' : '\\215', - '\216' : '\\216', '\217' : '\\217', '\220' : '\\220', - '\221' : '\\221', '\222' : '\\222', '\223' : '\\223', - '\224' : '\\224', '\225' : '\\225', '\226' : '\\226', - '\227' : '\\227', '\230' : '\\230', '\231' : '\\231', - '\232' : '\\232', '\233' : '\\233', '\234' : '\\234', - '\235' : '\\235', '\236' : '\\236', '\237' : '\\237', - '\240' : '\\240', '\241' : '\\241', '\242' : '\\242', - '\243' : '\\243', '\244' : '\\244', '\245' : '\\245', - '\246' : '\\246', '\247' : '\\247', '\250' : '\\250', - '\251' : '\\251', '\252' : '\\252', '\253' : '\\253', - '\254' : '\\254', '\255' : '\\255', '\256' : '\\256', - '\257' : '\\257', '\260' : '\\260', '\261' : '\\261', - '\262' : '\\262', '\263' : '\\263', '\264' : '\\264', - '\265' : '\\265', '\266' : '\\266', '\267' : '\\267', - '\270' : '\\270', '\271' : '\\271', '\272' : '\\272', - '\273' : '\\273', '\274' : '\\274', '\275' : '\\275', - '\276' : '\\276', '\277' : '\\277', '\300' : '\\300', - '\301' : '\\301', '\302' : '\\302', '\303' : '\\303', - '\304' : '\\304', '\305' : '\\305', '\306' : '\\306', - '\307' : '\\307', '\310' : '\\310', '\311' : '\\311', - '\312' : '\\312', '\313' : '\\313', '\314' : '\\314', - '\315' : '\\315', '\316' : '\\316', '\317' : '\\317', - '\320' : '\\320', '\321' : '\\321', '\322' : '\\322', - '\323' : '\\323', '\324' : '\\324', '\325' : '\\325', - '\326' : '\\326', '\327' : '\\327', '\330' : '\\330', - '\331' : '\\331', '\332' : '\\332', '\333' : '\\333', - '\334' : '\\334', '\335' : '\\335', '\336' : '\\336', - '\337' : '\\337', '\340' : '\\340', '\341' : '\\341', - '\342' : '\\342', '\343' : '\\343', '\344' : '\\344', - '\345' : '\\345', '\346' : '\\346', '\347' : '\\347', - '\350' : '\\350', '\351' : '\\351', '\352' : '\\352', - '\353' : '\\353', '\354' : '\\354', '\355' : '\\355', - '\356' : '\\356', '\357' : '\\357', '\360' : '\\360', - '\361' : '\\361', '\362' : '\\362', '\363' : '\\363', - '\364' : '\\364', '\365' : '\\365', '\366' : '\\366', - '\367' : '\\367', '\370' : '\\370', '\371' : '\\371', - '\372' : '\\372', '\373' : '\\373', '\374' : '\\374', - '\375' : '\\375', '\376' : '\\376', '\377' : '\\377' - } - -def _quote(str, LegalChars=_LegalChars): - r"""Quote a string for use in a cookie header. - - If the string does not need to be double-quoted, then just return the - string. Otherwise, surround the string in doublequotes and quote - (with a \) special characters. - """ - if all(c in LegalChars for c in str): - return str - else: - return '"' + _nulljoin(_Translator.get(s, s) for s in str) + '"' - - -_OctalPatt = re.compile(r"\\[0-3][0-7][0-7]") -_QuotePatt = re.compile(r"[\\].") - -def _unquote(mystr): - # If there aren't any doublequotes, - # then there can't be any special characters. See RFC 2109. - if len(mystr) < 2: - return mystr - if mystr[0] != '"' or mystr[-1] != '"': - return mystr - - # We have to assume that we must decode this string. - # Down to work. - - # Remove the "s - mystr = mystr[1:-1] - - # Check for special sequences. Examples: - # \012 --> \n - # \" --> " - # - i = 0 - n = len(mystr) - res = [] - while 0 <= i < n: - o_match = _OctalPatt.search(mystr, i) - q_match = _QuotePatt.search(mystr, i) - if not o_match and not q_match: # Neither matched - res.append(mystr[i:]) - break - # else: - j = k = -1 - if o_match: - j = o_match.start(0) - if q_match: - k = q_match.start(0) - if q_match and (not o_match or k < j): # QuotePatt matched - res.append(mystr[i:k]) - res.append(mystr[k+1]) - i = k + 2 - else: # OctalPatt matched - res.append(mystr[i:j]) - res.append(chr(int(mystr[j+1:j+4], 8))) - i = j + 4 - return _nulljoin(res) - -# The _getdate() routine is used to set the expiration time in the cookie's HTTP -# header. By default, _getdate() returns the current time in the appropriate -# "expires" format for a Set-Cookie header. The one optional argument is an -# offset from now, in seconds. For example, an offset of -3600 means "one hour -# ago". The offset may be a floating point number. -# - -_weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - -_monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - -def _getdate(future=0, weekdayname=_weekdayname, monthname=_monthname): - from time import gmtime, time - now = time() - year, month, day, hh, mm, ss, wd, y, z = gmtime(now + future) - return "%s, %02d %3s %4d %02d:%02d:%02d GMT" % \ - (weekdayname[wd], day, monthname[month], year, hh, mm, ss) - - -class Morsel(dict): - """A class to hold ONE (key, value) pair. - - In a cookie, each such pair may have several attributes, so this class is - used to keep the attributes associated with the appropriate key,value pair. - This class also includes a coded_value attribute, which is used to hold - the network representation of the value. This is most useful when Python - objects are pickled for network transit. - """ - # RFC 2109 lists these attributes as reserved: - # path comment domain - # max-age secure version - # - # For historical reasons, these attributes are also reserved: - # expires - # - # This is an extension from Microsoft: - # httponly - # - # This dictionary provides a mapping from the lowercase - # variant on the left to the appropriate traditional - # formatting on the right. - _reserved = { - "expires" : "expires", - "path" : "Path", - "comment" : "Comment", - "domain" : "Domain", - "max-age" : "Max-Age", - "secure" : "secure", - "httponly" : "httponly", - "version" : "Version", - } - - _flags = set(['secure', 'httponly']) - - def __init__(self): - # Set defaults - self.key = self.value = self.coded_value = None - - # Set default attributes - for key in self._reserved: - dict.__setitem__(self, key, "") - - def __setitem__(self, K, V): - K = K.lower() - if not K in self._reserved: - raise CookieError("Invalid Attribute %s" % K) - dict.__setitem__(self, K, V) - - def isReservedKey(self, K): - return K.lower() in self._reserved - - def set(self, key, val, coded_val, LegalChars=_LegalChars): - # First we verify that the key isn't a reserved word - # Second we make sure it only contains legal characters - if key.lower() in self._reserved: - raise CookieError("Attempt to set a reserved key: %s" % key) - if any(c not in LegalChars for c in key): - raise CookieError("Illegal key value: %s" % key) - - # It's a good key, so save it. - self.key = key - self.value = val - self.coded_value = coded_val - - def output(self, attrs=None, header="Set-Cookie:"): - return "%s %s" % (header, self.OutputString(attrs)) - - __str__ = output - - @as_native_str() - def __repr__(self): - if PY2 and isinstance(self.value, unicode): - val = str(self.value) # make it a newstr to remove the u prefix - else: - val = self.value - return '<%s: %s=%s>' % (self.__class__.__name__, - str(self.key), repr(val)) - - def js_output(self, attrs=None): - # Print javascript - return """ - - """ % (self.OutputString(attrs).replace('"', r'\"')) - - def OutputString(self, attrs=None): - # Build up our result - # - result = [] - append = result.append - - # First, the key=value pair - append("%s=%s" % (self.key, self.coded_value)) - - # Now add any defined attributes - if attrs is None: - attrs = self._reserved - items = sorted(self.items()) - for key, value in items: - if value == "": - continue - if key not in attrs: - continue - if key == "expires" and isinstance(value, int): - append("%s=%s" % (self._reserved[key], _getdate(value))) - elif key == "max-age" and isinstance(value, int): - append("%s=%d" % (self._reserved[key], value)) - elif key == "secure": - append(str(self._reserved[key])) - elif key == "httponly": - append(str(self._reserved[key])) - else: - append("%s=%s" % (self._reserved[key], value)) - - # Return the result - return _semispacejoin(result) - - -# -# Pattern for finding cookie -# -# This used to be strict parsing based on the RFC2109 and RFC2068 -# specifications. I have since discovered that MSIE 3.0x doesn't -# follow the character rules outlined in those specs. As a -# result, the parsing rules here are less strict. -# - -_LegalCharsPatt = r"[\w\d!#%&'~_`><@,:/\$\*\+\-\.\^\|\)\(\?\}\{\=]" -_CookiePattern = re.compile(r""" - (?x) # This is a verbose pattern - (?P # Start of group 'key' - """ + _LegalCharsPatt + r"""+? # Any word of at least one letter - ) # End of group 'key' - ( # Optional group: there may not be a value. - \s*=\s* # Equal Sign - (?P # Start of group 'val' - "(?:[^\\"]|\\.)*" # Any doublequoted string - | # or - \w{3},\s[\w\d\s-]{9,11}\s[\d:]{8}\sGMT # Special case for "expires" attr - | # or - """ + _LegalCharsPatt + r"""* # Any word or empty string - ) # End of group 'val' - )? # End of optional value group - \s* # Any number of spaces. - (\s+|;|$) # Ending either at space, semicolon, or EOS. - """, re.ASCII) # May be removed if safe. - - -# At long last, here is the cookie class. Using this class is almost just like -# using a dictionary. See this module's docstring for example usage. -# -class BaseCookie(dict): - """A container class for a set of Morsels.""" - - def value_decode(self, val): - """real_value, coded_value = value_decode(STRING) - Called prior to setting a cookie's value from the network - representation. The VALUE is the value read from HTTP - header. - Override this function to modify the behavior of cookies. - """ - return val, val - - def value_encode(self, val): - """real_value, coded_value = value_encode(VALUE) - Called prior to setting a cookie's value from the dictionary - representation. The VALUE is the value being assigned. - Override this function to modify the behavior of cookies. - """ - strval = str(val) - return strval, strval - - def __init__(self, input=None): - if input: - self.load(input) - - def __set(self, key, real_value, coded_value): - """Private method for setting a cookie's value""" - M = self.get(key, Morsel()) - M.set(key, real_value, coded_value) - dict.__setitem__(self, key, M) - - def __setitem__(self, key, value): - """Dictionary style assignment.""" - rval, cval = self.value_encode(value) - self.__set(key, rval, cval) - - def output(self, attrs=None, header="Set-Cookie:", sep="\015\012"): - """Return a string suitable for HTTP.""" - result = [] - items = sorted(self.items()) - for key, value in items: - result.append(value.output(attrs, header)) - return sep.join(result) - - __str__ = output - - @as_native_str() - def __repr__(self): - l = [] - items = sorted(self.items()) - for key, value in items: - if PY2 and isinstance(value.value, unicode): - val = str(value.value) # make it a newstr to remove the u prefix - else: - val = value.value - l.append('%s=%s' % (str(key), repr(val))) - return '<%s: %s>' % (self.__class__.__name__, _spacejoin(l)) - - def js_output(self, attrs=None): - """Return a string suitable for JavaScript.""" - result = [] - items = sorted(self.items()) - for key, value in items: - result.append(value.js_output(attrs)) - return _nulljoin(result) - - def load(self, rawdata): - """Load cookies from a string (presumably HTTP_COOKIE) or - from a dictionary. Loading cookies from a dictionary 'd' - is equivalent to calling: - map(Cookie.__setitem__, d.keys(), d.values()) - """ - if isinstance(rawdata, str): - self.__parse_string(rawdata) - else: - # self.update() wouldn't call our custom __setitem__ - for key, value in rawdata.items(): - self[key] = value - return - - def __parse_string(self, mystr, patt=_CookiePattern): - i = 0 # Our starting point - n = len(mystr) # Length of string - M = None # current morsel - - while 0 <= i < n: - # Start looking for a cookie - match = patt.search(mystr, i) - if not match: - # No more cookies - break - - key, value = match.group("key"), match.group("val") - - i = match.end(0) - - # Parse the key, value in case it's metainfo - if key[0] == "$": - # We ignore attributes which pertain to the cookie - # mechanism as a whole. See RFC 2109. - # (Does anyone care?) - if M: - M[key[1:]] = value - elif key.lower() in Morsel._reserved: - if M: - if value is None: - if key.lower() in Morsel._flags: - M[key] = True - else: - M[key] = _unquote(value) - elif value is not None: - rval, cval = self.value_decode(value) - self.__set(key, rval, cval) - M = self[key] - - -class SimpleCookie(BaseCookie): - """ - SimpleCookie supports strings as cookie values. When setting - the value using the dictionary assignment notation, SimpleCookie - calls the builtin str() to convert the value to a string. Values - received from HTTP are kept as strings. - """ - def value_decode(self, val): - return _unquote(val), val - - def value_encode(self, val): - strval = str(val) - return strval, _quote(strval) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/server.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/server.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/http/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/http/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1226 +0,0 @@ -"""HTTP server classes. - -From Python 3.3 - -Note: BaseHTTPRequestHandler doesn't implement any HTTP request; see -SimpleHTTPRequestHandler for simple implementations of GET, HEAD and POST, -and CGIHTTPRequestHandler for CGI scripts. - -It does, however, optionally implement HTTP/1.1 persistent connections, -as of version 0.3. - -Notes on CGIHTTPRequestHandler ------------------------------- - -This class implements GET and POST requests to cgi-bin scripts. - -If the os.fork() function is not present (e.g. on Windows), -subprocess.Popen() is used as a fallback, with slightly altered semantics. - -In all cases, the implementation is intentionally naive -- all -requests are executed synchronously. - -SECURITY WARNING: DON'T USE THIS CODE UNLESS YOU ARE INSIDE A FIREWALL --- it may execute arbitrary Python code or external programs. - -Note that status code 200 is sent prior to execution of a CGI script, so -scripts cannot send other status codes such as 302 (redirect). - -XXX To do: - -- log requests even later (to capture byte count) -- log user-agent header and other interesting goodies -- send error log to separate file -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future import utils -from future.builtins import * - - -# See also: -# -# HTTP Working Group T. Berners-Lee -# INTERNET-DRAFT R. T. Fielding -# H. Frystyk Nielsen -# Expires September 8, 1995 March 8, 1995 -# -# URL: http://www.ics.uci.edu/pub/ietf/http/draft-ietf-http-v10-spec-00.txt -# -# and -# -# Network Working Group R. Fielding -# Request for Comments: 2616 et al -# Obsoletes: 2068 June 1999 -# Category: Standards Track -# -# URL: http://www.faqs.org/rfcs/rfc2616.html - -# Log files -# --------- -# -# Here's a quote from the NCSA httpd docs about log file format. -# -# | The logfile format is as follows. Each line consists of: -# | -# | host rfc931 authuser [DD/Mon/YYYY:hh:mm:ss] "request" ddd bbbb -# | -# | host: Either the DNS name or the IP number of the remote client -# | rfc931: Any information returned by identd for this person, -# | - otherwise. -# | authuser: If user sent a userid for authentication, the user name, -# | - otherwise. -# | DD: Day -# | Mon: Month (calendar name) -# | YYYY: Year -# | hh: hour (24-hour format, the machine's timezone) -# | mm: minutes -# | ss: seconds -# | request: The first line of the HTTP request as sent by the client. -# | ddd: the status code returned by the server, - if not available. -# | bbbb: the total number of bytes sent, -# | *not including the HTTP/1.0 header*, - if not available -# | -# | You can determine the name of the file accessed through request. -# -# (Actually, the latter is only true if you know the server configuration -# at the time the request was made!) - -__version__ = "0.6" - -__all__ = ["HTTPServer", "BaseHTTPRequestHandler"] - -from future.backports import html -from future.backports.http import client as http_client -from future.backports.urllib import parse as urllib_parse -from future.backports import socketserver - -import io -import mimetypes -import os -import posixpath -import select -import shutil -import socket # For gethostbyaddr() -import sys -import time -import copy -import argparse - - -# Default error message template -DEFAULT_ERROR_MESSAGE = """\ - - - - - Error response - - -

Error response

-

Error code: %(code)d

-

Message: %(message)s.

-

Error code explanation: %(code)s - %(explain)s.

- - -""" - -DEFAULT_ERROR_CONTENT_TYPE = "text/html;charset=utf-8" - -def _quote_html(html): - return html.replace("&", "&").replace("<", "<").replace(">", ">") - -class HTTPServer(socketserver.TCPServer): - - allow_reuse_address = 1 # Seems to make sense in testing environment - - def server_bind(self): - """Override server_bind to store the server name.""" - socketserver.TCPServer.server_bind(self) - host, port = self.socket.getsockname()[:2] - self.server_name = socket.getfqdn(host) - self.server_port = port - - -class BaseHTTPRequestHandler(socketserver.StreamRequestHandler): - - """HTTP request handler base class. - - The following explanation of HTTP serves to guide you through the - code as well as to expose any misunderstandings I may have about - HTTP (so you don't need to read the code to figure out I'm wrong - :-). - - HTTP (HyperText Transfer Protocol) is an extensible protocol on - top of a reliable stream transport (e.g. TCP/IP). The protocol - recognizes three parts to a request: - - 1. One line identifying the request type and path - 2. An optional set of RFC-822-style headers - 3. An optional data part - - The headers and data are separated by a blank line. - - The first line of the request has the form - - - - where is a (case-sensitive) keyword such as GET or POST, - is a string containing path information for the request, - and should be the string "HTTP/1.0" or "HTTP/1.1". - is encoded using the URL encoding scheme (using %xx to signify - the ASCII character with hex code xx). - - The specification specifies that lines are separated by CRLF but - for compatibility with the widest range of clients recommends - servers also handle LF. Similarly, whitespace in the request line - is treated sensibly (allowing multiple spaces between components - and allowing trailing whitespace). - - Similarly, for output, lines ought to be separated by CRLF pairs - but most clients grok LF characters just fine. - - If the first line of the request has the form - - - - (i.e. is left out) then this is assumed to be an HTTP - 0.9 request; this form has no optional headers and data part and - the reply consists of just the data. - - The reply form of the HTTP 1.x protocol again has three parts: - - 1. One line giving the response code - 2. An optional set of RFC-822-style headers - 3. The data - - Again, the headers and data are separated by a blank line. - - The response code line has the form - - - - where is the protocol version ("HTTP/1.0" or "HTTP/1.1"), - is a 3-digit response code indicating success or - failure of the request, and is an optional - human-readable string explaining what the response code means. - - This server parses the request and the headers, and then calls a - function specific to the request type (). Specifically, - a request SPAM will be handled by a method do_SPAM(). If no - such method exists the server sends an error response to the - client. If it exists, it is called with no arguments: - - do_SPAM() - - Note that the request name is case sensitive (i.e. SPAM and spam - are different requests). - - The various request details are stored in instance variables: - - - client_address is the client IP address in the form (host, - port); - - - command, path and version are the broken-down request line; - - - headers is an instance of email.message.Message (or a derived - class) containing the header information; - - - rfile is a file object open for reading positioned at the - start of the optional input data part; - - - wfile is a file object open for writing. - - IT IS IMPORTANT TO ADHERE TO THE PROTOCOL FOR WRITING! - - The first thing to be written must be the response line. Then - follow 0 or more header lines, then a blank line, and then the - actual data (if any). The meaning of the header lines depends on - the command executed by the server; in most cases, when data is - returned, there should be at least one header line of the form - - Content-type: / - - where and should be registered MIME types, - e.g. "text/html" or "text/plain". - - """ - - # The Python system version, truncated to its first component. - sys_version = "Python/" + sys.version.split()[0] - - # The server software version. You may want to override this. - # The format is multiple whitespace-separated strings, - # where each string is of the form name[/version]. - server_version = "BaseHTTP/" + __version__ - - error_message_format = DEFAULT_ERROR_MESSAGE - error_content_type = DEFAULT_ERROR_CONTENT_TYPE - - # The default request version. This only affects responses up until - # the point where the request line is parsed, so it mainly decides what - # the client gets back when sending a malformed request line. - # Most web servers default to HTTP 0.9, i.e. don't send a status line. - default_request_version = "HTTP/0.9" - - def parse_request(self): - """Parse a request (internal). - - The request should be stored in self.raw_requestline; the results - are in self.command, self.path, self.request_version and - self.headers. - - Return True for success, False for failure; on failure, an - error is sent back. - - """ - self.command = None # set in case of error on the first line - self.request_version = version = self.default_request_version - self.close_connection = 1 - requestline = str(self.raw_requestline, 'iso-8859-1') - requestline = requestline.rstrip('\r\n') - self.requestline = requestline - words = requestline.split() - if len(words) == 3: - command, path, version = words - if version[:5] != 'HTTP/': - self.send_error(400, "Bad request version (%r)" % version) - return False - try: - base_version_number = version.split('/', 1)[1] - version_number = base_version_number.split(".") - # RFC 2145 section 3.1 says there can be only one "." and - # - major and minor numbers MUST be treated as - # separate integers; - # - HTTP/2.4 is a lower version than HTTP/2.13, which in - # turn is lower than HTTP/12.3; - # - Leading zeros MUST be ignored by recipients. - if len(version_number) != 2: - raise ValueError - version_number = int(version_number[0]), int(version_number[1]) - except (ValueError, IndexError): - self.send_error(400, "Bad request version (%r)" % version) - return False - if version_number >= (1, 1) and self.protocol_version >= "HTTP/1.1": - self.close_connection = 0 - if version_number >= (2, 0): - self.send_error(505, - "Invalid HTTP Version (%s)" % base_version_number) - return False - elif len(words) == 2: - command, path = words - self.close_connection = 1 - if command != 'GET': - self.send_error(400, - "Bad HTTP/0.9 request type (%r)" % command) - return False - elif not words: - return False - else: - self.send_error(400, "Bad request syntax (%r)" % requestline) - return False - self.command, self.path, self.request_version = command, path, version - - # Examine the headers and look for a Connection directive. - try: - self.headers = http_client.parse_headers(self.rfile, - _class=self.MessageClass) - except http_client.LineTooLong: - self.send_error(400, "Line too long") - return False - - conntype = self.headers.get('Connection', "") - if conntype.lower() == 'close': - self.close_connection = 1 - elif (conntype.lower() == 'keep-alive' and - self.protocol_version >= "HTTP/1.1"): - self.close_connection = 0 - # Examine the headers and look for an Expect directive - expect = self.headers.get('Expect', "") - if (expect.lower() == "100-continue" and - self.protocol_version >= "HTTP/1.1" and - self.request_version >= "HTTP/1.1"): - if not self.handle_expect_100(): - return False - return True - - def handle_expect_100(self): - """Decide what to do with an "Expect: 100-continue" header. - - If the client is expecting a 100 Continue response, we must - respond with either a 100 Continue or a final response before - waiting for the request body. The default is to always respond - with a 100 Continue. You can behave differently (for example, - reject unauthorized requests) by overriding this method. - - This method should either return True (possibly after sending - a 100 Continue response) or send an error response and return - False. - - """ - self.send_response_only(100) - self.flush_headers() - return True - - def handle_one_request(self): - """Handle a single HTTP request. - - You normally don't need to override this method; see the class - __doc__ string for information on how to handle specific HTTP - commands such as GET and POST. - - """ - try: - self.raw_requestline = self.rfile.readline(65537) - if len(self.raw_requestline) > 65536: - self.requestline = '' - self.request_version = '' - self.command = '' - self.send_error(414) - return - if not self.raw_requestline: - self.close_connection = 1 - return - if not self.parse_request(): - # An error code has been sent, just exit - return - mname = 'do_' + self.command - if not hasattr(self, mname): - self.send_error(501, "Unsupported method (%r)" % self.command) - return - method = getattr(self, mname) - method() - self.wfile.flush() #actually send the response if not already done. - except socket.timeout as e: - #a read or a write timed out. Discard this connection - self.log_error("Request timed out: %r", e) - self.close_connection = 1 - return - - def handle(self): - """Handle multiple requests if necessary.""" - self.close_connection = 1 - - self.handle_one_request() - while not self.close_connection: - self.handle_one_request() - - def send_error(self, code, message=None): - """Send and log an error reply. - - Arguments are the error code, and a detailed message. - The detailed message defaults to the short entry matching the - response code. - - This sends an error response (so it must be called before any - output has been generated), logs the error, and finally sends - a piece of HTML explaining the error to the user. - - """ - - try: - shortmsg, longmsg = self.responses[code] - except KeyError: - shortmsg, longmsg = '???', '???' - if message is None: - message = shortmsg - explain = longmsg - self.log_error("code %d, message %s", code, message) - # using _quote_html to prevent Cross Site Scripting attacks (see bug #1100201) - content = (self.error_message_format % - {'code': code, 'message': _quote_html(message), 'explain': explain}) - self.send_response(code, message) - self.send_header("Content-Type", self.error_content_type) - self.send_header('Connection', 'close') - self.end_headers() - if self.command != 'HEAD' and code >= 200 and code not in (204, 304): - self.wfile.write(content.encode('UTF-8', 'replace')) - - def send_response(self, code, message=None): - """Add the response header to the headers buffer and log the - response code. - - Also send two standard headers with the server software - version and the current date. - - """ - self.log_request(code) - self.send_response_only(code, message) - self.send_header('Server', self.version_string()) - self.send_header('Date', self.date_time_string()) - - def send_response_only(self, code, message=None): - """Send the response header only.""" - if message is None: - if code in self.responses: - message = self.responses[code][0] - else: - message = '' - if self.request_version != 'HTTP/0.9': - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append(("%s %d %s\r\n" % - (self.protocol_version, code, message)).encode( - 'latin-1', 'strict')) - - def send_header(self, keyword, value): - """Send a MIME header to the headers buffer.""" - if self.request_version != 'HTTP/0.9': - if not hasattr(self, '_headers_buffer'): - self._headers_buffer = [] - self._headers_buffer.append( - ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) - - if keyword.lower() == 'connection': - if value.lower() == 'close': - self.close_connection = 1 - elif value.lower() == 'keep-alive': - self.close_connection = 0 - - def end_headers(self): - """Send the blank line ending the MIME headers.""" - if self.request_version != 'HTTP/0.9': - self._headers_buffer.append(b"\r\n") - self.flush_headers() - - def flush_headers(self): - if hasattr(self, '_headers_buffer'): - self.wfile.write(b"".join(self._headers_buffer)) - self._headers_buffer = [] - - def log_request(self, code='-', size='-'): - """Log an accepted request. - - This is called by send_response(). - - """ - - self.log_message('"%s" %s %s', - self.requestline, str(code), str(size)) - - def log_error(self, format, *args): - """Log an error. - - This is called when a request cannot be fulfilled. By - default it passes the message on to log_message(). - - Arguments are the same as for log_message(). - - XXX This should go to the separate error log. - - """ - - self.log_message(format, *args) - - def log_message(self, format, *args): - """Log an arbitrary message. - - This is used by all other logging functions. Override - it if you have specific logging wishes. - - The first argument, FORMAT, is a format string for the - message to be logged. If the format string contains - any % escapes requiring parameters, they should be - specified as subsequent arguments (it's just like - printf!). - - The client ip and current date/time are prefixed to - every message. - - """ - - sys.stderr.write("%s - - [%s] %s\n" % - (self.address_string(), - self.log_date_time_string(), - format%args)) - - def version_string(self): - """Return the server software version string.""" - return self.server_version + ' ' + self.sys_version - - def date_time_string(self, timestamp=None): - """Return the current date and time formatted for a message header.""" - if timestamp is None: - timestamp = time.time() - year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) - s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( - self.weekdayname[wd], - day, self.monthname[month], year, - hh, mm, ss) - return s - - def log_date_time_string(self): - """Return the current time formatted for logging.""" - now = time.time() - year, month, day, hh, mm, ss, x, y, z = time.localtime(now) - s = "%02d/%3s/%04d %02d:%02d:%02d" % ( - day, self.monthname[month], year, hh, mm, ss) - return s - - weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] - - monthname = [None, - 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', - 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] - - def address_string(self): - """Return the client address.""" - - return self.client_address[0] - - # Essentially static class variables - - # The version of the HTTP protocol we support. - # Set this to HTTP/1.1 to enable automatic keepalive - protocol_version = "HTTP/1.0" - - # MessageClass used to parse headers - MessageClass = http_client.HTTPMessage - - # Table mapping response codes to messages; entries have the - # form {code: (shortmessage, longmessage)}. - # See RFC 2616 and 6585. - responses = { - 100: ('Continue', 'Request received, please continue'), - 101: ('Switching Protocols', - 'Switching to new protocol; obey Upgrade header'), - - 200: ('OK', 'Request fulfilled, document follows'), - 201: ('Created', 'Document created, URL follows'), - 202: ('Accepted', - 'Request accepted, processing continues off-line'), - 203: ('Non-Authoritative Information', 'Request fulfilled from cache'), - 204: ('No Content', 'Request fulfilled, nothing follows'), - 205: ('Reset Content', 'Clear input form for further input.'), - 206: ('Partial Content', 'Partial content follows.'), - - 300: ('Multiple Choices', - 'Object has several resources -- see URI list'), - 301: ('Moved Permanently', 'Object moved permanently -- see URI list'), - 302: ('Found', 'Object moved temporarily -- see URI list'), - 303: ('See Other', 'Object moved -- see Method and URL list'), - 304: ('Not Modified', - 'Document has not changed since given time'), - 305: ('Use Proxy', - 'You must use proxy specified in Location to access this ' - 'resource.'), - 307: ('Temporary Redirect', - 'Object moved temporarily -- see URI list'), - - 400: ('Bad Request', - 'Bad request syntax or unsupported method'), - 401: ('Unauthorized', - 'No permission -- see authorization schemes'), - 402: ('Payment Required', - 'No payment -- see charging schemes'), - 403: ('Forbidden', - 'Request forbidden -- authorization will not help'), - 404: ('Not Found', 'Nothing matches the given URI'), - 405: ('Method Not Allowed', - 'Specified method is invalid for this resource.'), - 406: ('Not Acceptable', 'URI not available in preferred format.'), - 407: ('Proxy Authentication Required', 'You must authenticate with ' - 'this proxy before proceeding.'), - 408: ('Request Timeout', 'Request timed out; try again later.'), - 409: ('Conflict', 'Request conflict.'), - 410: ('Gone', - 'URI no longer exists and has been permanently removed.'), - 411: ('Length Required', 'Client must specify Content-Length.'), - 412: ('Precondition Failed', 'Precondition in headers is false.'), - 413: ('Request Entity Too Large', 'Entity is too large.'), - 414: ('Request-URI Too Long', 'URI is too long.'), - 415: ('Unsupported Media Type', 'Entity body in unsupported format.'), - 416: ('Requested Range Not Satisfiable', - 'Cannot satisfy request range.'), - 417: ('Expectation Failed', - 'Expect condition could not be satisfied.'), - 428: ('Precondition Required', - 'The origin server requires the request to be conditional.'), - 429: ('Too Many Requests', 'The user has sent too many requests ' - 'in a given amount of time ("rate limiting").'), - 431: ('Request Header Fields Too Large', 'The server is unwilling to ' - 'process the request because its header fields are too large.'), - - 500: ('Internal Server Error', 'Server got itself in trouble'), - 501: ('Not Implemented', - 'Server does not support this operation'), - 502: ('Bad Gateway', 'Invalid responses from another server/proxy.'), - 503: ('Service Unavailable', - 'The server cannot process the request due to a high load'), - 504: ('Gateway Timeout', - 'The gateway server did not receive a timely response'), - 505: ('HTTP Version Not Supported', 'Cannot fulfill request.'), - 511: ('Network Authentication Required', - 'The client needs to authenticate to gain network access.'), - } - - -class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): - - """Simple HTTP request handler with GET and HEAD commands. - - This serves files from the current directory and any of its - subdirectories. The MIME type for files is determined by - calling the .guess_type() method. - - The GET and HEAD requests are identical except that the HEAD - request omits the actual contents of the file. - - """ - - server_version = "SimpleHTTP/" + __version__ - - def do_GET(self): - """Serve a GET request.""" - f = self.send_head() - if f: - self.copyfile(f, self.wfile) - f.close() - - def do_HEAD(self): - """Serve a HEAD request.""" - f = self.send_head() - if f: - f.close() - - def send_head(self): - """Common code for GET and HEAD commands. - - This sends the response code and MIME headers. - - Return value is either a file object (which has to be copied - to the outputfile by the caller unless the command was HEAD, - and must be closed by the caller under all circumstances), or - None, in which case the caller has nothing further to do. - - """ - path = self.translate_path(self.path) - f = None - if os.path.isdir(path): - if not self.path.endswith('/'): - # redirect browser - doing basically what apache does - self.send_response(301) - self.send_header("Location", self.path + "/") - self.end_headers() - return None - for index in "index.html", "index.htm": - index = os.path.join(path, index) - if os.path.exists(index): - path = index - break - else: - return self.list_directory(path) - ctype = self.guess_type(path) - try: - f = open(path, 'rb') - except IOError: - self.send_error(404, "File not found") - return None - self.send_response(200) - self.send_header("Content-type", ctype) - fs = os.fstat(f.fileno()) - self.send_header("Content-Length", str(fs[6])) - self.send_header("Last-Modified", self.date_time_string(fs.st_mtime)) - self.end_headers() - return f - - def list_directory(self, path): - """Helper to produce a directory listing (absent index.html). - - Return value is either a file object, or None (indicating an - error). In either case, the headers are sent, making the - interface the same as for send_head(). - - """ - try: - list = os.listdir(path) - except os.error: - self.send_error(404, "No permission to list directory") - return None - list.sort(key=lambda a: a.lower()) - r = [] - displaypath = html.escape(urllib_parse.unquote(self.path)) - enc = sys.getfilesystemencoding() - title = 'Directory listing for %s' % displaypath - r.append('') - r.append('\n') - r.append('' % enc) - r.append('%s\n' % title) - r.append('\n

%s

' % title) - r.append('
\n
    ') - for name in list: - fullname = os.path.join(path, name) - displayname = linkname = name - # Append / for directories or @ for symbolic links - if os.path.isdir(fullname): - displayname = name + "/" - linkname = name + "/" - if os.path.islink(fullname): - displayname = name + "@" - # Note: a link to a directory displays with @ and links with / - r.append('
  • %s
  • ' - % (urllib_parse.quote(linkname), html.escape(displayname))) - # # Use this instead: - # r.append('
  • %s
  • ' - # % (urllib.quote(linkname), cgi.escape(displayname))) - r.append('
\n
\n\n\n') - encoded = '\n'.join(r).encode(enc) - f = io.BytesIO() - f.write(encoded) - f.seek(0) - self.send_response(200) - self.send_header("Content-type", "text/html; charset=%s" % enc) - self.send_header("Content-Length", str(len(encoded))) - self.end_headers() - return f - - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - # abandon query parameters - path = path.split('?',1)[0] - path = path.split('#',1)[0] - path = posixpath.normpath(urllib_parse.unquote(path)) - words = path.split('/') - words = filter(None, words) - path = os.getcwd() - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - if word in (os.curdir, os.pardir): continue - path = os.path.join(path, word) - return path - - def copyfile(self, source, outputfile): - """Copy all data between two file objects. - - The SOURCE argument is a file object open for reading - (or anything with a read() method) and the DESTINATION - argument is a file object open for writing (or - anything with a write() method). - - The only reason for overriding this would be to change - the block size or perhaps to replace newlines by CRLF - -- note however that this the default server uses this - to copy binary data as well. - - """ - shutil.copyfileobj(source, outputfile) - - def guess_type(self, path): - """Guess the type of a file. - - Argument is a PATH (a filename). - - Return value is a string of the form type/subtype, - usable for a MIME Content-type header. - - The default implementation looks the file's extension - up in the table self.extensions_map, using application/octet-stream - as a default; however it would be permissible (if - slow) to look inside the data to make a better guess. - - """ - - base, ext = posixpath.splitext(path) - if ext in self.extensions_map: - return self.extensions_map[ext] - ext = ext.lower() - if ext in self.extensions_map: - return self.extensions_map[ext] - else: - return self.extensions_map[''] - - if not mimetypes.inited: - mimetypes.init() # try to read system mime.types - extensions_map = mimetypes.types_map.copy() - extensions_map.update({ - '': 'application/octet-stream', # Default - '.py': 'text/plain', - '.c': 'text/plain', - '.h': 'text/plain', - }) - - -# Utilities for CGIHTTPRequestHandler - -def _url_collapse_path(path): - """ - Given a URL path, remove extra '/'s and '.' path elements and collapse - any '..' references and returns a colllapsed path. - - Implements something akin to RFC-2396 5.2 step 6 to parse relative paths. - The utility of this function is limited to is_cgi method and helps - preventing some security attacks. - - Returns: A tuple of (head, tail) where tail is everything after the final / - and head is everything before it. Head will always start with a '/' and, - if it contains anything else, never have a trailing '/'. - - Raises: IndexError if too many '..' occur within the path. - - """ - # Similar to os.path.split(os.path.normpath(path)) but specific to URL - # path semantics rather than local operating system semantics. - path_parts = path.split('/') - head_parts = [] - for part in path_parts[:-1]: - if part == '..': - head_parts.pop() # IndexError if more '..' than prior parts - elif part and part != '.': - head_parts.append( part ) - if path_parts: - tail_part = path_parts.pop() - if tail_part: - if tail_part == '..': - head_parts.pop() - tail_part = '' - elif tail_part == '.': - tail_part = '' - else: - tail_part = '' - - splitpath = ('/' + '/'.join(head_parts), tail_part) - collapsed_path = "/".join(splitpath) - - return collapsed_path - - - -nobody = None - -def nobody_uid(): - """Internal routine to get nobody's uid""" - global nobody - if nobody: - return nobody - try: - import pwd - except ImportError: - return -1 - try: - nobody = pwd.getpwnam('nobody')[2] - except KeyError: - nobody = 1 + max(x[2] for x in pwd.getpwall()) - return nobody - - -def executable(path): - """Test for executable file.""" - return os.access(path, os.X_OK) - - -class CGIHTTPRequestHandler(SimpleHTTPRequestHandler): - - """Complete HTTP server with GET, HEAD and POST commands. - - GET and HEAD also support running CGI scripts. - - The POST command is *only* implemented for CGI scripts. - - """ - - # Determine platform specifics - have_fork = hasattr(os, 'fork') - - # Make rfile unbuffered -- we need to read one line and then pass - # the rest to a subprocess, so we can't use buffered input. - rbufsize = 0 - - def do_POST(self): - """Serve a POST request. - - This is only implemented for CGI scripts. - - """ - - if self.is_cgi(): - self.run_cgi() - else: - self.send_error(501, "Can only POST to CGI scripts") - - def send_head(self): - """Version of send_head that support CGI scripts""" - if self.is_cgi(): - return self.run_cgi() - else: - return SimpleHTTPRequestHandler.send_head(self) - - def is_cgi(self): - """Test whether self.path corresponds to a CGI script. - - Returns True and updates the cgi_info attribute to the tuple - (dir, rest) if self.path requires running a CGI script. - Returns False otherwise. - - If any exception is raised, the caller should assume that - self.path was rejected as invalid and act accordingly. - - The default implementation tests whether the normalized url - path begins with one of the strings in self.cgi_directories - (and the next character is a '/' or the end of the string). - - """ - collapsed_path = _url_collapse_path(self.path) - dir_sep = collapsed_path.find('/', 1) - head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:] - if head in self.cgi_directories: - self.cgi_info = head, tail - return True - return False - - - cgi_directories = ['/cgi-bin', '/htbin'] - - def is_executable(self, path): - """Test whether argument path is an executable file.""" - return executable(path) - - def is_python(self, path): - """Test whether argument path is a Python script.""" - head, tail = os.path.splitext(path) - return tail.lower() in (".py", ".pyw") - - def run_cgi(self): - """Execute a CGI script.""" - path = self.path - dir, rest = self.cgi_info - - i = path.find('/', len(dir) + 1) - while i >= 0: - nextdir = path[:i] - nextrest = path[i+1:] - - scriptdir = self.translate_path(nextdir) - if os.path.isdir(scriptdir): - dir, rest = nextdir, nextrest - i = path.find('/', len(dir) + 1) - else: - break - - # find an explicit query string, if present. - i = rest.rfind('?') - if i >= 0: - rest, query = rest[:i], rest[i+1:] - else: - query = '' - - # dissect the part after the directory name into a script name & - # a possible additional path, to be stored in PATH_INFO. - i = rest.find('/') - if i >= 0: - script, rest = rest[:i], rest[i:] - else: - script, rest = rest, '' - - scriptname = dir + '/' + script - scriptfile = self.translate_path(scriptname) - if not os.path.exists(scriptfile): - self.send_error(404, "No such CGI script (%r)" % scriptname) - return - if not os.path.isfile(scriptfile): - self.send_error(403, "CGI script is not a plain file (%r)" % - scriptname) - return - ispy = self.is_python(scriptname) - if self.have_fork or not ispy: - if not self.is_executable(scriptfile): - self.send_error(403, "CGI script is not executable (%r)" % - scriptname) - return - - # Reference: http://hoohoo.ncsa.uiuc.edu/cgi/env.html - # XXX Much of the following could be prepared ahead of time! - env = copy.deepcopy(os.environ) - env['SERVER_SOFTWARE'] = self.version_string() - env['SERVER_NAME'] = self.server.server_name - env['GATEWAY_INTERFACE'] = 'CGI/1.1' - env['SERVER_PROTOCOL'] = self.protocol_version - env['SERVER_PORT'] = str(self.server.server_port) - env['REQUEST_METHOD'] = self.command - uqrest = urllib_parse.unquote(rest) - env['PATH_INFO'] = uqrest - env['PATH_TRANSLATED'] = self.translate_path(uqrest) - env['SCRIPT_NAME'] = scriptname - if query: - env['QUERY_STRING'] = query - env['REMOTE_ADDR'] = self.client_address[0] - authorization = self.headers.get("authorization") - if authorization: - authorization = authorization.split() - if len(authorization) == 2: - import base64, binascii - env['AUTH_TYPE'] = authorization[0] - if authorization[0].lower() == "basic": - try: - authorization = authorization[1].encode('ascii') - if utils.PY3: - # In Py3.3, was: - authorization = base64.decodebytes(authorization).\ - decode('ascii') - else: - # Backport to Py2.7: - authorization = base64.decodestring(authorization).\ - decode('ascii') - except (binascii.Error, UnicodeError): - pass - else: - authorization = authorization.split(':') - if len(authorization) == 2: - env['REMOTE_USER'] = authorization[0] - # XXX REMOTE_IDENT - if self.headers.get('content-type') is None: - env['CONTENT_TYPE'] = self.headers.get_content_type() - else: - env['CONTENT_TYPE'] = self.headers['content-type'] - length = self.headers.get('content-length') - if length: - env['CONTENT_LENGTH'] = length - referer = self.headers.get('referer') - if referer: - env['HTTP_REFERER'] = referer - accept = [] - for line in self.headers.getallmatchingheaders('accept'): - if line[:1] in "\t\n\r ": - accept.append(line.strip()) - else: - accept = accept + line[7:].split(',') - env['HTTP_ACCEPT'] = ','.join(accept) - ua = self.headers.get('user-agent') - if ua: - env['HTTP_USER_AGENT'] = ua - co = filter(None, self.headers.get_all('cookie', [])) - cookie_str = ', '.join(co) - if cookie_str: - env['HTTP_COOKIE'] = cookie_str - # XXX Other HTTP_* headers - # Since we're setting the env in the parent, provide empty - # values to override previously set values - for k in ('QUERY_STRING', 'REMOTE_HOST', 'CONTENT_LENGTH', - 'HTTP_USER_AGENT', 'HTTP_COOKIE', 'HTTP_REFERER'): - env.setdefault(k, "") - - self.send_response(200, "Script output follows") - self.flush_headers() - - decoded_query = query.replace('+', ' ') - - if self.have_fork: - # Unix -- fork as we should - args = [script] - if '=' not in decoded_query: - args.append(decoded_query) - nobody = nobody_uid() - self.wfile.flush() # Always flush before forking - pid = os.fork() - if pid != 0: - # Parent - pid, sts = os.waitpid(pid, 0) - # throw away additional data [see bug #427345] - while select.select([self.rfile], [], [], 0)[0]: - if not self.rfile.read(1): - break - if sts: - self.log_error("CGI script exit status %#x", sts) - return - # Child - try: - try: - os.setuid(nobody) - except os.error: - pass - os.dup2(self.rfile.fileno(), 0) - os.dup2(self.wfile.fileno(), 1) - os.execve(scriptfile, args, env) - except: - self.server.handle_error(self.request, self.client_address) - os._exit(127) - - else: - # Non-Unix -- use subprocess - import subprocess - cmdline = [scriptfile] - if self.is_python(scriptfile): - interp = sys.executable - if interp.lower().endswith("w.exe"): - # On Windows, use python.exe, not pythonw.exe - interp = interp[:-5] + interp[-4:] - cmdline = [interp, '-u'] + cmdline - if '=' not in query: - cmdline.append(query) - self.log_message("command: %s", subprocess.list2cmdline(cmdline)) - try: - nbytes = int(length) - except (TypeError, ValueError): - nbytes = 0 - p = subprocess.Popen(cmdline, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - env = env - ) - if self.command.lower() == "post" and nbytes > 0: - data = self.rfile.read(nbytes) - else: - data = None - # throw away additional data [see bug #427345] - while select.select([self.rfile._sock], [], [], 0)[0]: - if not self.rfile._sock.recv(1): - break - stdout, stderr = p.communicate(data) - self.wfile.write(stdout) - if stderr: - self.log_error('%s', stderr) - p.stderr.close() - p.stdout.close() - status = p.returncode - if status: - self.log_error("CGI script exit status %#x", status) - else: - self.log_message("CGI script exited OK") - - -def test(HandlerClass = BaseHTTPRequestHandler, - ServerClass = HTTPServer, protocol="HTTP/1.0", port=8000): - """Test the HTTP request handler class. - - This runs an HTTP server on port 8000 (or the first command line - argument). - - """ - server_address = ('', port) - - HandlerClass.protocol_version = protocol - httpd = ServerClass(server_address, HandlerClass) - - sa = httpd.socket.getsockname() - print("Serving HTTP on", sa[0], "port", sa[1], "...") - try: - httpd.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - httpd.server_close() - sys.exit(0) - -if __name__ == '__main__': - parser = argparse.ArgumentParser() - parser.add_argument('--cgi', action='store_true', - help='Run as CGI Server') - parser.add_argument('port', action='store', - default=8000, type=int, - nargs='?', - help='Specify alternate port [default: 8000]') - args = parser.parse_args() - if args.cgi: - test(HandlerClass=CGIHTTPRequestHandler, port=args.port) - else: - test(HandlerClass=SimpleHTTPRequestHandler, port=args.port) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -""" -future.backports package -""" - -from __future__ import absolute_import - -import sys - -__future_module__ = True -from future.standard_library import import_top_level_modules - - -if sys.version_info[0] == 3: - import_top_level_modules() - - -from .misc import (ceil, - OrderedDict, - Counter, - ChainMap, - check_output, - count, - recursive_repr, - _count_elements, - cmp_to_key - ) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/_markupbase.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/_markupbase.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/_markupbase.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/_markupbase.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,422 +0,0 @@ -"""Shared support for scanning document type declarations in HTML and XHTML. - -Backported for python-future from Python 3.3. Reason: ParserBase is an -old-style class in the Python 2.7 source of markupbase.py, which I suspect -might be the cause of sporadic unit-test failures on travis-ci.org with -test_htmlparser.py. The test failures look like this: - - ====================================================================== - -ERROR: test_attr_entity_replacement (future.tests.test_htmlparser.AttributesStrictTestCase) - ----------------------------------------------------------------------- - -Traceback (most recent call last): - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 661, in test_attr_entity_replacement - [("starttag", "a", [("b", "&><\"'")])]) - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 93, in _run_check - collector = self.get_collector() - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 617, in get_collector - return EventCollector(strict=True) - File "/home/travis/build/edschofield/python-future/future/tests/test_htmlparser.py", line 27, in __init__ - html.parser.HTMLParser.__init__(self, *args, **kw) - File "/home/travis/build/edschofield/python-future/future/backports/html/parser.py", line 135, in __init__ - self.reset() - File "/home/travis/build/edschofield/python-future/future/backports/html/parser.py", line 143, in reset - _markupbase.ParserBase.reset(self) - -TypeError: unbound method reset() must be called with ParserBase instance as first argument (got EventCollector instance instead) - -This module is used as a foundation for the html.parser module. It has no -documented public API and should not be used directly. - -""" - -import re - -_declname_match = re.compile(r'[a-zA-Z][-_.a-zA-Z0-9]*\s*').match -_declstringlit_match = re.compile(r'(\'[^\']*\'|"[^"]*")\s*').match -_commentclose = re.compile(r'--\s*>') -_markedsectionclose = re.compile(r']\s*]\s*>') - -# An analysis of the MS-Word extensions is available at -# http://www.planetpublish.com/xmlarena/xap/Thursday/WordtoXML.pdf - -_msmarkedsectionclose = re.compile(r']\s*>') - -del re - - -class ParserBase(object): - """Parser base class which provides some common support methods used - by the SGML/HTML and XHTML parsers.""" - - def __init__(self): - if self.__class__ is ParserBase: - raise RuntimeError( - "_markupbase.ParserBase must be subclassed") - - def error(self, message): - raise NotImplementedError( - "subclasses of ParserBase must override error()") - - def reset(self): - self.lineno = 1 - self.offset = 0 - - def getpos(self): - """Return current line number and offset.""" - return self.lineno, self.offset - - # Internal -- update line number and offset. This should be - # called for each piece of data exactly once, in order -- in other - # words the concatenation of all the input strings to this - # function should be exactly the entire input. - def updatepos(self, i, j): - if i >= j: - return j - rawdata = self.rawdata - nlines = rawdata.count("\n", i, j) - if nlines: - self.lineno = self.lineno + nlines - pos = rawdata.rindex("\n", i, j) # Should not fail - self.offset = j-(pos+1) - else: - self.offset = self.offset + j-i - return j - - _decl_otherchars = '' - - # Internal -- parse declaration (for use by subclasses). - def parse_declaration(self, i): - # This is some sort of declaration; in "HTML as - # deployed," this should only be the document type - # declaration (""). - # ISO 8879:1986, however, has more complex - # declaration syntax for elements in , including: - # --comment-- - # [marked section] - # name in the following list: ENTITY, DOCTYPE, ELEMENT, - # ATTLIST, NOTATION, SHORTREF, USEMAP, - # LINKTYPE, LINK, IDLINK, USELINK, SYSTEM - rawdata = self.rawdata - j = i + 2 - assert rawdata[i:j] == "": - # the empty comment - return j + 1 - if rawdata[j:j+1] in ("-", ""): - # Start of comment followed by buffer boundary, - # or just a buffer boundary. - return -1 - # A simple, practical version could look like: ((name|stringlit) S*) + '>' - n = len(rawdata) - if rawdata[j:j+2] == '--': #comment - # Locate --.*-- as the body of the comment - return self.parse_comment(i) - elif rawdata[j] == '[': #marked section - # Locate [statusWord [...arbitrary SGML...]] as the body of the marked section - # Where statusWord is one of TEMP, CDATA, IGNORE, INCLUDE, RCDATA - # Note that this is extended by Microsoft Office "Save as Web" function - # to include [if...] and [endif]. - return self.parse_marked_section(i) - else: #all other declaration elements - decltype, j = self._scan_name(j, i) - if j < 0: - return j - if decltype == "doctype": - self._decl_otherchars = '' - while j < n: - c = rawdata[j] - if c == ">": - # end of declaration syntax - data = rawdata[i+2:j] - if decltype == "doctype": - self.handle_decl(data) - else: - # According to the HTML5 specs sections "8.2.4.44 Bogus - # comment state" and "8.2.4.45 Markup declaration open - # state", a comment token should be emitted. - # Calling unknown_decl provides more flexibility though. - self.unknown_decl(data) - return j + 1 - if c in "\"'": - m = _declstringlit_match(rawdata, j) - if not m: - return -1 # incomplete - j = m.end() - elif c in "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ": - name, j = self._scan_name(j, i) - elif c in self._decl_otherchars: - j = j + 1 - elif c == "[": - # this could be handled in a separate doctype parser - if decltype == "doctype": - j = self._parse_doctype_subset(j + 1, i) - elif decltype in set(["attlist", "linktype", "link", "element"]): - # must tolerate []'d groups in a content model in an element declaration - # also in data attribute specifications of attlist declaration - # also link type declaration subsets in linktype declarations - # also link attribute specification lists in link declarations - self.error("unsupported '[' char in %s declaration" % decltype) - else: - self.error("unexpected '[' char in declaration") - else: - self.error( - "unexpected %r char in declaration" % rawdata[j]) - if j < 0: - return j - return -1 # incomplete - - # Internal -- parse a marked section - # Override this to handle MS-word extension syntax content - def parse_marked_section(self, i, report=1): - rawdata= self.rawdata - assert rawdata[i:i+3] == ' ending - match= _markedsectionclose.search(rawdata, i+3) - elif sectName in set(["if", "else", "endif"]): - # look for MS Office ]> ending - match= _msmarkedsectionclose.search(rawdata, i+3) - else: - self.error('unknown status keyword %r in marked section' % rawdata[i+3:j]) - if not match: - return -1 - if report: - j = match.start(0) - self.unknown_decl(rawdata[i+3: j]) - return match.end(0) - - # Internal -- parse comment, return length or -1 if not terminated - def parse_comment(self, i, report=1): - rawdata = self.rawdata - if rawdata[i:i+4] != '| UnixStreamServer | - +-----------+ +------------------+ - | - v - +-----------+ +--------------------+ - | UDPServer |------->| UnixDatagramServer | - +-----------+ +--------------------+ - -Note that UnixDatagramServer derives from UDPServer, not from -UnixStreamServer -- the only difference between an IP and a Unix -stream server is the address family, which is simply repeated in both -unix server classes. - -Forking and threading versions of each type of server can be created -using the ForkingMixIn and ThreadingMixIn mix-in classes. For -instance, a threading UDP server class is created as follows: - - class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass - -The Mix-in class must come first, since it overrides a method defined -in UDPServer! Setting the various member variables also changes -the behavior of the underlying server mechanism. - -To implement a service, you must derive a class from -BaseRequestHandler and redefine its handle() method. You can then run -various versions of the service by combining one of the server classes -with your request handler class. - -The request handler class must be different for datagram or stream -services. This can be hidden by using the request handler -subclasses StreamRequestHandler or DatagramRequestHandler. - -Of course, you still have to use your head! - -For instance, it makes no sense to use a forking server if the service -contains state in memory that can be modified by requests (since the -modifications in the child process would never reach the initial state -kept in the parent process and passed to each child). In this case, -you can use a threading server, but you will probably have to use -locks to avoid two requests that come in nearly simultaneous to apply -conflicting changes to the server state. - -On the other hand, if you are building e.g. an HTTP server, where all -data is stored externally (e.g. in the file system), a synchronous -class will essentially render the service "deaf" while one request is -being handled -- which may be for a very long time if a client is slow -to read all the data it has requested. Here a threading or forking -server is appropriate. - -In some cases, it may be appropriate to process part of a request -synchronously, but to finish processing in a forked child depending on -the request data. This can be implemented by using a synchronous -server and doing an explicit fork in the request handler class -handle() method. - -Another approach to handling multiple simultaneous requests in an -environment that supports neither threads nor fork (or where these are -too expensive or inappropriate for the service) is to maintain an -explicit table of partially finished requests and to use select() to -decide which request to work on next (or whether to handle a new -incoming request). This is particularly important for stream services -where each client can potentially be connected for a long time (if -threads or subprocesses cannot be used). - -Future work: -- Standard classes for Sun RPC (which uses either UDP or TCP) -- Standard mix-in classes to implement various authentication - and encryption schemes -- Standard framework for select-based multiplexing - -XXX Open problems: -- What to do with out-of-band data? - -BaseServer: -- split generic "request" functionality out into BaseServer class. - Copyright (C) 2000 Luke Kenneth Casson Leighton - - example: read entries from a SQL database (requires overriding - get_request() to return a table entry from the database). - entry is processed by a RequestHandlerClass. - -""" - -# Author of the BaseServer patch: Luke Kenneth Casson Leighton - -# XXX Warning! -# There is a test suite for this module, but it cannot be run by the -# standard regression test. -# To run it manually, run Lib/test/test_socketserver.py. - -from __future__ import (absolute_import, print_function) - -__version__ = "0.4" - - -import socket -import select -import sys -import os -import errno -try: - import threading -except ImportError: - import dummy_threading as threading - -__all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer", - "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler", - "StreamRequestHandler","DatagramRequestHandler", - "ThreadingMixIn", "ForkingMixIn"] -if hasattr(socket, "AF_UNIX"): - __all__.extend(["UnixStreamServer","UnixDatagramServer", - "ThreadingUnixStreamServer", - "ThreadingUnixDatagramServer"]) - -def _eintr_retry(func, *args): - """restart a system call interrupted by EINTR""" - while True: - try: - return func(*args) - except OSError as e: - if e.errno != errno.EINTR: - raise - -class BaseServer(object): - - """Base class for server classes. - - Methods for the caller: - - - __init__(server_address, RequestHandlerClass) - - serve_forever(poll_interval=0.5) - - shutdown() - - handle_request() # if you do not use serve_forever() - - fileno() -> int # for select() - - Methods that may be overridden: - - - server_bind() - - server_activate() - - get_request() -> request, client_address - - handle_timeout() - - verify_request(request, client_address) - - server_close() - - process_request(request, client_address) - - shutdown_request(request) - - close_request(request) - - service_actions() - - handle_error() - - Methods for derived classes: - - - finish_request(request, client_address) - - Class variables that may be overridden by derived classes or - instances: - - - timeout - - address_family - - socket_type - - allow_reuse_address - - Instance variables: - - - RequestHandlerClass - - socket - - """ - - timeout = None - - def __init__(self, server_address, RequestHandlerClass): - """Constructor. May be extended, do not override.""" - self.server_address = server_address - self.RequestHandlerClass = RequestHandlerClass - self.__is_shut_down = threading.Event() - self.__shutdown_request = False - - def server_activate(self): - """Called by constructor to activate the server. - - May be overridden. - - """ - pass - - def serve_forever(self, poll_interval=0.5): - """Handle one request at a time until shutdown. - - Polls for shutdown every poll_interval seconds. Ignores - self.timeout. If you need to do periodic tasks, do them in - another thread. - """ - self.__is_shut_down.clear() - try: - while not self.__shutdown_request: - # XXX: Consider using another file descriptor or - # connecting to the socket to wake this up instead of - # polling. Polling reduces our responsiveness to a - # shutdown request and wastes cpu at all other times. - r, w, e = _eintr_retry(select.select, [self], [], [], - poll_interval) - if self in r: - self._handle_request_noblock() - - self.service_actions() - finally: - self.__shutdown_request = False - self.__is_shut_down.set() - - def shutdown(self): - """Stops the serve_forever loop. - - Blocks until the loop has finished. This must be called while - serve_forever() is running in another thread, or it will - deadlock. - """ - self.__shutdown_request = True - self.__is_shut_down.wait() - - def service_actions(self): - """Called by the serve_forever() loop. - - May be overridden by a subclass / Mixin to implement any code that - needs to be run during the loop. - """ - pass - - # The distinction between handling, getting, processing and - # finishing a request is fairly arbitrary. Remember: - # - # - handle_request() is the top-level call. It calls - # select, get_request(), verify_request() and process_request() - # - get_request() is different for stream or datagram sockets - # - process_request() is the place that may fork a new process - # or create a new thread to finish the request - # - finish_request() instantiates the request handler class; - # this constructor will handle the request all by itself - - def handle_request(self): - """Handle one request, possibly blocking. - - Respects self.timeout. - """ - # Support people who used socket.settimeout() to escape - # handle_request before self.timeout was available. - timeout = self.socket.gettimeout() - if timeout is None: - timeout = self.timeout - elif self.timeout is not None: - timeout = min(timeout, self.timeout) - fd_sets = _eintr_retry(select.select, [self], [], [], timeout) - if not fd_sets[0]: - self.handle_timeout() - return - self._handle_request_noblock() - - def _handle_request_noblock(self): - """Handle one request, without blocking. - - I assume that select.select has returned that the socket is - readable before this function was called, so there should be - no risk of blocking in get_request(). - """ - try: - request, client_address = self.get_request() - except socket.error: - return - if self.verify_request(request, client_address): - try: - self.process_request(request, client_address) - except: - self.handle_error(request, client_address) - self.shutdown_request(request) - - def handle_timeout(self): - """Called if no new request arrives within self.timeout. - - Overridden by ForkingMixIn. - """ - pass - - def verify_request(self, request, client_address): - """Verify the request. May be overridden. - - Return True if we should proceed with this request. - - """ - return True - - def process_request(self, request, client_address): - """Call finish_request. - - Overridden by ForkingMixIn and ThreadingMixIn. - - """ - self.finish_request(request, client_address) - self.shutdown_request(request) - - def server_close(self): - """Called to clean-up the server. - - May be overridden. - - """ - pass - - def finish_request(self, request, client_address): - """Finish one request by instantiating RequestHandlerClass.""" - self.RequestHandlerClass(request, client_address, self) - - def shutdown_request(self, request): - """Called to shutdown and close an individual request.""" - self.close_request(request) - - def close_request(self, request): - """Called to clean up an individual request.""" - pass - - def handle_error(self, request, client_address): - """Handle an error gracefully. May be overridden. - - The default is to print a traceback and continue. - - """ - print('-'*40) - print('Exception happened during processing of request from', end=' ') - print(client_address) - import traceback - traceback.print_exc() # XXX But this goes to stderr! - print('-'*40) - - -class TCPServer(BaseServer): - - """Base class for various socket-based server classes. - - Defaults to synchronous IP stream (i.e., TCP). - - Methods for the caller: - - - __init__(server_address, RequestHandlerClass, bind_and_activate=True) - - serve_forever(poll_interval=0.5) - - shutdown() - - handle_request() # if you don't use serve_forever() - - fileno() -> int # for select() - - Methods that may be overridden: - - - server_bind() - - server_activate() - - get_request() -> request, client_address - - handle_timeout() - - verify_request(request, client_address) - - process_request(request, client_address) - - shutdown_request(request) - - close_request(request) - - handle_error() - - Methods for derived classes: - - - finish_request(request, client_address) - - Class variables that may be overridden by derived classes or - instances: - - - timeout - - address_family - - socket_type - - request_queue_size (only for stream sockets) - - allow_reuse_address - - Instance variables: - - - server_address - - RequestHandlerClass - - socket - - """ - - address_family = socket.AF_INET - - socket_type = socket.SOCK_STREAM - - request_queue_size = 5 - - allow_reuse_address = False - - def __init__(self, server_address, RequestHandlerClass, bind_and_activate=True): - """Constructor. May be extended, do not override.""" - BaseServer.__init__(self, server_address, RequestHandlerClass) - self.socket = socket.socket(self.address_family, - self.socket_type) - if bind_and_activate: - self.server_bind() - self.server_activate() - - def server_bind(self): - """Called by constructor to bind the socket. - - May be overridden. - - """ - if self.allow_reuse_address: - self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - self.socket.bind(self.server_address) - self.server_address = self.socket.getsockname() - - def server_activate(self): - """Called by constructor to activate the server. - - May be overridden. - - """ - self.socket.listen(self.request_queue_size) - - def server_close(self): - """Called to clean-up the server. - - May be overridden. - - """ - self.socket.close() - - def fileno(self): - """Return socket file number. - - Interface required by select(). - - """ - return self.socket.fileno() - - def get_request(self): - """Get the request and client address from the socket. - - May be overridden. - - """ - return self.socket.accept() - - def shutdown_request(self, request): - """Called to shutdown and close an individual request.""" - try: - #explicitly shutdown. socket.close() merely releases - #the socket and waits for GC to perform the actual close. - request.shutdown(socket.SHUT_WR) - except socket.error: - pass #some platforms may raise ENOTCONN here - self.close_request(request) - - def close_request(self, request): - """Called to clean up an individual request.""" - request.close() - - -class UDPServer(TCPServer): - - """UDP server class.""" - - allow_reuse_address = False - - socket_type = socket.SOCK_DGRAM - - max_packet_size = 8192 - - def get_request(self): - data, client_addr = self.socket.recvfrom(self.max_packet_size) - return (data, self.socket), client_addr - - def server_activate(self): - # No need to call listen() for UDP. - pass - - def shutdown_request(self, request): - # No need to shutdown anything. - self.close_request(request) - - def close_request(self, request): - # No need to close anything. - pass - -class ForkingMixIn(object): - - """Mix-in class to handle each request in a new process.""" - - timeout = 300 - active_children = None - max_children = 40 - - def collect_children(self): - """Internal routine to wait for children that have exited.""" - if self.active_children is None: return - while len(self.active_children) >= self.max_children: - # XXX: This will wait for any child process, not just ones - # spawned by this library. This could confuse other - # libraries that expect to be able to wait for their own - # children. - try: - pid, status = os.waitpid(0, 0) - except os.error: - pid = None - if pid not in self.active_children: continue - self.active_children.remove(pid) - - # XXX: This loop runs more system calls than it ought - # to. There should be a way to put the active_children into a - # process group and then use os.waitpid(-pgid) to wait for any - # of that set, but I couldn't find a way to allocate pgids - # that couldn't collide. - for child in self.active_children: - try: - pid, status = os.waitpid(child, os.WNOHANG) - except os.error: - pid = None - if not pid: continue - try: - self.active_children.remove(pid) - except ValueError as e: - raise ValueError('%s. x=%d and list=%r' % (e.message, pid, - self.active_children)) - - def handle_timeout(self): - """Wait for zombies after self.timeout seconds of inactivity. - - May be extended, do not override. - """ - self.collect_children() - - def service_actions(self): - """Collect the zombie child processes regularly in the ForkingMixIn. - - service_actions is called in the BaseServer's serve_forver loop. - """ - self.collect_children() - - def process_request(self, request, client_address): - """Fork a new subprocess to process the request.""" - pid = os.fork() - if pid: - # Parent process - if self.active_children is None: - self.active_children = [] - self.active_children.append(pid) - self.close_request(request) - return - else: - # Child process. - # This must never return, hence os._exit()! - try: - self.finish_request(request, client_address) - self.shutdown_request(request) - os._exit(0) - except: - try: - self.handle_error(request, client_address) - self.shutdown_request(request) - finally: - os._exit(1) - - -class ThreadingMixIn(object): - """Mix-in class to handle each request in a new thread.""" - - # Decides how threads will act upon termination of the - # main process - daemon_threads = False - - def process_request_thread(self, request, client_address): - """Same as in BaseServer but as a thread. - - In addition, exception handling is done here. - - """ - try: - self.finish_request(request, client_address) - self.shutdown_request(request) - except: - self.handle_error(request, client_address) - self.shutdown_request(request) - - def process_request(self, request, client_address): - """Start a new thread to process the request.""" - t = threading.Thread(target = self.process_request_thread, - args = (request, client_address)) - t.daemon = self.daemon_threads - t.start() - - -class ForkingUDPServer(ForkingMixIn, UDPServer): pass -class ForkingTCPServer(ForkingMixIn, TCPServer): pass - -class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass -class ThreadingTCPServer(ThreadingMixIn, TCPServer): pass - -if hasattr(socket, 'AF_UNIX'): - - class UnixStreamServer(TCPServer): - address_family = socket.AF_UNIX - - class UnixDatagramServer(UDPServer): - address_family = socket.AF_UNIX - - class ThreadingUnixStreamServer(ThreadingMixIn, UnixStreamServer): pass - - class ThreadingUnixDatagramServer(ThreadingMixIn, UnixDatagramServer): pass - -class BaseRequestHandler(object): - - """Base class for request handler classes. - - This class is instantiated for each request to be handled. The - constructor sets the instance variables request, client_address - and server, and then calls the handle() method. To implement a - specific service, all you need to do is to derive a class which - defines a handle() method. - - The handle() method can find the request as self.request, the - client address as self.client_address, and the server (in case it - needs access to per-server information) as self.server. Since a - separate instance is created for each request, the handle() method - can define arbitrary other instance variariables. - - """ - - def __init__(self, request, client_address, server): - self.request = request - self.client_address = client_address - self.server = server - self.setup() - try: - self.handle() - finally: - self.finish() - - def setup(self): - pass - - def handle(self): - pass - - def finish(self): - pass - - -# The following two classes make it possible to use the same service -# class for stream or datagram servers. -# Each class sets up these instance variables: -# - rfile: a file object from which receives the request is read -# - wfile: a file object to which the reply is written -# When the handle() method returns, wfile is flushed properly - - -class StreamRequestHandler(BaseRequestHandler): - - """Define self.rfile and self.wfile for stream sockets.""" - - # Default buffer sizes for rfile, wfile. - # We default rfile to buffered because otherwise it could be - # really slow for large data (a getc() call per byte); we make - # wfile unbuffered because (a) often after a write() we want to - # read and we need to flush the line; (b) big writes to unbuffered - # files are typically optimized by stdio even when big reads - # aren't. - rbufsize = -1 - wbufsize = 0 - - # A timeout to apply to the request socket, if not None. - timeout = None - - # Disable nagle algorithm for this socket, if True. - # Use only when wbufsize != 0, to avoid small packets. - disable_nagle_algorithm = False - - def setup(self): - self.connection = self.request - if self.timeout is not None: - self.connection.settimeout(self.timeout) - if self.disable_nagle_algorithm: - self.connection.setsockopt(socket.IPPROTO_TCP, - socket.TCP_NODELAY, True) - self.rfile = self.connection.makefile('rb', self.rbufsize) - self.wfile = self.connection.makefile('wb', self.wbufsize) - - def finish(self): - if not self.wfile.closed: - try: - self.wfile.flush() - except socket.error: - # An final socket error may have occurred here, such as - # the local error ECONNABORTED. - pass - self.wfile.close() - self.rfile.close() - - -class DatagramRequestHandler(BaseRequestHandler): - - # XXX Regrettably, I cannot get this working on Linux; - # s.recvfrom() doesn't return a meaningful client address. - - """Define self.rfile and self.wfile for datagram sockets.""" - - def setup(self): - from io import BytesIO - self.packet, self.socket = self.request - self.rfile = BytesIO(self.packet) - self.wfile = BytesIO() - - def finish(self): - self.socket.sendto(self.wfile.getvalue(), self.client_address) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/badcert.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/badcert.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/badcert.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/badcert.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L -opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH -fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB -AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU -D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA -IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM -oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0 -ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/ -loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j -oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA -z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq -ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV -q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU= ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -Just bad cert data ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -MIICXwIBAAKBgQC8ddrhm+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9L -opdJhTvbGfEj0DQs1IE8M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVH -fhi/VwovESJlaBOp+WMnfhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQAB -AoGBAK0FZpaKj6WnJZN0RqhhK+ggtBWwBnc0U/ozgKz2j1s3fsShYeiGtW6CK5nU -D1dZ5wzhbGThI7LiOXDvRucc9n7vUgi0alqPQ/PFodPxAN/eEYkmXQ7W2k7zwsDA -IUK0KUhktQbLu8qF/m8qM86ba9y9/9YkXuQbZ3COl5ahTZrhAkEA301P08RKv3KM -oXnGU2UHTuJ1MAD2hOrPxjD4/wxA/39EWG9bZczbJyggB4RHu0I3NOSFjAm3HQm0 -ANOu5QK9owJBANgOeLfNNcF4pp+UikRFqxk5hULqRAWzVxVrWe85FlPm0VVmHbb/ -loif7mqjU8o1jTd/LM7RD9f2usZyE2psaw8CQQCNLhkpX3KO5kKJmS9N7JMZSc4j -oog58yeYO8BBqKKzpug0LXuQultYv2K4veaIO04iL9VLe5z9S/Q1jaCHBBuXAkEA -z8gjGoi1AOp6PBBLZNsncCvcV/0aC+1se4HxTNo2+duKSDnbq+ljqOM+E7odU+Nq -ewvIWOG//e8fssd0mq3HywJBAJ8l/c8GVmrpFTx8r/nZ2Pyyjt3dH1widooDXYSV -q6Gbf41Llo5sYAtmxdndTLASuHKecacTgZVhy0FryZpLKrU= ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -Just bad cert data ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/badkey.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/badkey.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/badkey.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/badkey.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Bad Key, though the cert should be OK ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD -VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x -IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT -U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1 -NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl -bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m -dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj -aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh -m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8 -M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn -fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC -AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb -08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx -CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/ -iHkC6gGdBJhogs4= ------END CERTIFICATE----- ------BEGIN RSA PRIVATE KEY----- -Bad Key, though the cert should be OK ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIICpzCCAhCgAwIBAgIJAP+qStv1cIGNMA0GCSqGSIb3DQEBBQUAMIGJMQswCQYD -VQQGEwJVUzERMA8GA1UECBMIRGVsYXdhcmUxEzARBgNVBAcTCldpbG1pbmd0b24x -IzAhBgNVBAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMQwwCgYDVQQLEwNT -U0wxHzAdBgNVBAMTFnNvbWVtYWNoaW5lLnB5dGhvbi5vcmcwHhcNMDcwODI3MTY1 -NDUwWhcNMTMwMjE2MTY1NDUwWjCBiTELMAkGA1UEBhMCVVMxETAPBgNVBAgTCERl -bGF3YXJlMRMwEQYDVQQHEwpXaWxtaW5ndG9uMSMwIQYDVQQKExpQeXRob24gU29m -dHdhcmUgRm91bmRhdGlvbjEMMAoGA1UECxMDU1NMMR8wHQYDVQQDExZzb21lbWFj -aGluZS5weXRob24ub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC8ddrh -m+LutBvjYcQlnH21PPIseJ1JVG2HMmN2CmZk2YukO+9LopdJhTvbGfEj0DQs1IE8 -M+kTUyOmuKfVrFMKwtVeCJphrAnhoz7TYOuLBSqt7lVHfhi/VwovESJlaBOp+WMn -fhcduPEYHYx/6cnVapIkZnLt30zu2um+DzA9jQIDAQABoxUwEzARBglghkgBhvhC -AQEEBAMCBkAwDQYJKoZIhvcNAQEFBQADgYEAF4Q5BVqmCOLv1n8je/Jw9K669VXb -08hyGzQhkemEBYQd6fzQ9A/1ZzHkJKb1P6yreOLSEh4KcxYPyrLRC1ll8nr5OlCx -CMhKkTnR6qBsdNV0XtdU2+N25hqW+Ma4ZeqsN/iiJVCGNOZGnvQuvCAGWF8+J/f/ -iHkC6gGdBJhogs4= ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/dh512.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/dh512.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/dh512.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/dh512.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ ------BEGIN DH PARAMETERS----- -MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak -XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC ------END DH PARAMETERS----- - -These are the 512 bit DH parameters from "Assigned Number for SKIP Protocols" -(http://www.skip-vpn.org/spec/numbers.html). -See there for how they were generated. -Note that g is not a generator, but this is not a problem since p is a safe prime. diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/https_svn_python_org_root.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/https_svn_python_org_root.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/https_svn_python_org_root.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/https_svn_python_org_root.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 -IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB -IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA -Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO -BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi -MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ -ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC -CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ -8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6 -zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y -fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7 -w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc -G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k -epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q -laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ -QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU -fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826 -YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w -ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY -gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe -MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0 -IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy -dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw -czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0 -dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl -aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC -AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg -b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB -ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc -nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg -18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c -gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl -Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY -sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T -SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF -CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum -GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk -zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW -omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -""" -test package backported for python-future. - -Its primary purpose is to allow use of "import test.support" for running -the Python standard library unit tests using the new Python 3 stdlib -import location. - -Python 3 renamed test.test_support to test.support. -""" diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/keycert2.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/keycert2.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/keycert2.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/keycert2.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAJnsJZVrppL+W5I9 -zGQrrawWwE5QJpBK9nWw17mXrZ03R1cD9BamLGivVISbPlRlAVnZBEyh1ATpsB7d -CUQ+WHEvALquvx4+Yw5l+fXeiYRjrLRBYZuVy8yNtXzU3iWcGObcYRkUdiXdOyP7 -sLF2YZHRvQZpzgDBKkrraeQ81w21AgMBAAECgYBEm7n07FMHWlE+0kT0sXNsLYfy -YE+QKZnJw9WkaDN+zFEEPELkhZVt5BjsMraJr6v2fIEqF0gGGJPkbenffVq2B5dC -lWUOxvJHufMK4sM3Cp6s/gOp3LP+QkzVnvJSfAyZU6l+4PGX5pLdUsXYjPxgzjzL -S36tF7/2Uv1WePyLUQJBAMsPhYzUXOPRgmbhcJiqi9A9c3GO8kvSDYTCKt3VMnqz -HBn6MQ4VQasCD1F+7jWTI0FU/3vdw8non/Fj8hhYqZcCQQDCDRdvmZqDiZnpMqDq -L6ZSrLTVtMvZXZbgwForaAD9uHj51TME7+eYT7EG2YCgJTXJ4YvRJEnPNyskwdKt -vTSTAkEAtaaN/vyemEJ82BIGStwONNw0ILsSr5cZ9tBHzqiA/tipY+e36HRFiXhP -QcU9zXlxyWkDH8iz9DSAmE2jbfoqwwJANlMJ65E543cjIlitGcKLMnvtCCLcKpb7 -xSG0XJB6Lo11OKPJ66jp0gcFTSCY1Lx2CXVd+gfJrfwI1Pp562+bhwJBAJ9IfDPU -R8OpO9v1SGd8x33Owm7uXOpB9d63/T70AD1QOXjKUC4eXYbt0WWfWuny/RNPRuyh -w7DXSfUF+kPKolU= ------END PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIICXTCCAcagAwIBAgIJAIO3upAG445fMA0GCSqGSIb3DQEBBQUAMGIxCzAJBgNV -BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xFTATBgNVBAMTDGZha2Vob3N0bmFtZTAeFw0x -MDEwMDkxNTAxMDBaFw0yMDEwMDYxNTAxMDBaMGIxCzAJBgNVBAYTAlhZMRcwFQYD -VQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZv -dW5kYXRpb24xFTATBgNVBAMTDGZha2Vob3N0bmFtZTCBnzANBgkqhkiG9w0BAQEF -AAOBjQAwgYkCgYEAmewllWumkv5bkj3MZCutrBbATlAmkEr2dbDXuZetnTdHVwP0 -FqYsaK9UhJs+VGUBWdkETKHUBOmwHt0JRD5YcS8Auq6/Hj5jDmX59d6JhGOstEFh -m5XLzI21fNTeJZwY5txhGRR2Jd07I/uwsXZhkdG9BmnOAMEqSutp5DzXDbUCAwEA -AaMbMBkwFwYDVR0RBBAwDoIMZmFrZWhvc3RuYW1lMA0GCSqGSIb3DQEBBQUAA4GB -AH+iMClLLGSaKWgwXsmdVo4FhTZZHo8Uprrtg3N9FxEeE50btpDVQysgRt5ias3K -m+bME9zbKwvbVWD5zZdjus4pDgzwF/iHyccL8JyYhxOvS/9zmvAtFXj/APIIbZFp -IT75d9f88ScIGEtknZQejnrdhB64tYki/EqluiuKBqKD ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/keycert.passwd.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/keycert.passwd.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/keycert.passwd.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/keycert.passwd.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,1A8D9D2A02EC698A - -kJYbfZ8L0sfe9Oty3gw0aloNnY5E8fegRfQLZlNoxTl6jNt0nIwI8kDJ36CZgR9c -u3FDJm/KqrfUoz8vW+qEnWhSG7QPX2wWGPHd4K94Yz/FgrRzZ0DoK7XxXq9gOtVA -AVGQhnz32p+6WhfGsCr9ArXEwRZrTk/FvzEPaU5fHcoSkrNVAGX8IpSVkSDwEDQr -Gv17+cfk99UV1OCza6yKHoFkTtrC+PZU71LomBabivS2Oc4B9hYuSR2hF01wTHP+ -YlWNagZOOVtNz4oKK9x9eNQpmfQXQvPPTfusexKIbKfZrMvJoxcm1gfcZ0H/wK6P -6wmXSG35qMOOztCZNtperjs1wzEBXznyK8QmLcAJBjkfarABJX9vBEzZV0OUKhy+ -noORFwHTllphbmydLhu6ehLUZMHPhzAS5UN7srtpSN81eerDMy0RMUAwA7/PofX1 -94Me85Q8jP0PC9ETdsJcPqLzAPETEYu0ELewKRcrdyWi+tlLFrpE5KT/s5ecbl9l -7B61U4Kfd1PIXc/siINhU3A3bYK+845YyUArUOnKf1kEox7p1RpD7yFqVT04lRTo -cibNKATBusXSuBrp2G6GNuhWEOSafWCKJQAzgCYIp6ZTV2khhMUGppc/2H3CF6cO -zX0KtlPVZC7hLkB6HT8SxYUwF1zqWY7+/XPPdc37MeEZ87Q3UuZwqORLY+Z0hpgt -L5JXBCoklZhCAaN2GqwFLXtGiRSRFGY7xXIhbDTlE65Wv1WGGgDLMKGE1gOz3yAo -2jjG1+yAHJUdE69XTFHSqSkvaloA1W03LdMXZ9VuQJ/ySXCie6ABAQ== ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV -BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw -MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH -Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k -YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw -gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7 -6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt -pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw -FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd -BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G -lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1 -CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/keycert.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/keycert.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/keycert.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/keycert.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANtb0+YrKuxevGpm -LrjaUhZSgz6zFAmuGFmKmUbdjmfv9zSmmdsQIksK++jK0Be9LeZy20j6ahOfuVa0 -ufEmPoP7Fy4hXegKZR9cCWcIe/A6H2xWF1IIJLRTLaU8ol/I7T+um5HD5AwAwNPP -USNU0Eegmvp+xxWu3NX2m1Veot85AgMBAAECgYA3ZdZ673X0oexFlq7AAmrutkHt -CL7LvwrpOiaBjhyTxTeSNWzvtQBkIU8DOI0bIazA4UreAFffwtvEuPmonDb3F+Iq -SMAu42XcGyVZEl+gHlTPU9XRX7nTOXVt+MlRRRxL6t9GkGfUAXI3XxJDXW3c0vBK -UL9xqD8cORXOfE06rQJBAP8mEX1ERkR64Ptsoe4281vjTlNfIbs7NMPkUnrn9N/Y -BLhjNIfQ3HFZG8BTMLfX7kCS9D593DW5tV4Z9BP/c6cCQQDcFzCcVArNh2JSywOQ -ZfTfRbJg/Z5Lt9Fkngv1meeGNPgIMLN8Sg679pAOOWmzdMO3V706rNPzSVMME7E5 -oPIfAkEA8pDddarP5tCvTTgUpmTFbakm0KoTZm2+FzHcnA4jRh+XNTjTOv98Y6Ik -eO5d1ZnKXseWvkZncQgxfdnMqqpj5wJAcNq/RVne1DbYlwWchT2Si65MYmmJ8t+F -0mcsULqjOnEMwf5e+ptq5LzwbyrHZYq5FNk7ocufPv/ZQrcSSC+cFwJBAKvOJByS -x56qyGeZLOQlWS2JS3KJo59XuLFGqcbgN9Om9xFa41Yb4N9NvplFivsvZdw3m1Q/ -SPIXQuT8RMPDVNQ= ------END PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV -BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw -MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH -Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k -YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw -gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7 -6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt -pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw -FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd -BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G -lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1 -CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/nokia.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/nokia.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/nokia.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/nokia.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -# Certificate for projects.developer.nokia.com:443 (see issue 13034) ------BEGIN CERTIFICATE----- -MIIFLDCCBBSgAwIBAgIQLubqdkCgdc7lAF9NfHlUmjANBgkqhkiG9w0BAQUFADCB -vDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMR8wHQYDVQQL -ExZWZXJpU2lnbiBUcnVzdCBOZXR3b3JrMTswOQYDVQQLEzJUZXJtcyBvZiB1c2Ug -YXQgaHR0cHM6Ly93d3cudmVyaXNpZ24uY29tL3JwYSAoYykxMDE2MDQGA1UEAxMt -VmVyaVNpZ24gQ2xhc3MgMyBJbnRlcm5hdGlvbmFsIFNlcnZlciBDQSAtIEczMB4X -DTExMDkyMTAwMDAwMFoXDTEyMDkyMDIzNTk1OVowcTELMAkGA1UEBhMCRkkxDjAM -BgNVBAgTBUVzcG9vMQ4wDAYDVQQHFAVFc3BvbzEOMAwGA1UEChQFTm9raWExCzAJ -BgNVBAsUAkJJMSUwIwYDVQQDFBxwcm9qZWN0cy5kZXZlbG9wZXIubm9raWEuY29t -MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCr92w1bpHYSYxUEx8N/8Iddda2 -lYi+aXNtQfV/l2Fw9Ykv3Ipw4nLeGTj18FFlAZgMdPRlgrzF/NNXGw/9l3/qKdow -CypkQf8lLaxb9Ze1E/KKmkRJa48QTOqvo6GqKuTI6HCeGlG1RxDb8YSKcQWLiytn -yj3Wp4MgRQO266xmMQIDAQABo4IB9jCCAfIwQQYDVR0RBDowOIIccHJvamVjdHMu -ZGV2ZWxvcGVyLm5va2lhLmNvbYIYcHJvamVjdHMuZm9ydW0ubm9raWEuY29tMAkG -A1UdEwQCMAAwCwYDVR0PBAQDAgWgMEEGA1UdHwQ6MDgwNqA0oDKGMGh0dHA6Ly9T -VlJJbnRsLUczLWNybC52ZXJpc2lnbi5jb20vU1ZSSW50bEczLmNybDBEBgNVHSAE -PTA7MDkGC2CGSAGG+EUBBxcDMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LnZl -cmlzaWduLmNvbS9ycGEwKAYDVR0lBCEwHwYJYIZIAYb4QgQBBggrBgEFBQcDAQYI -KwYBBQUHAwIwcgYIKwYBBQUHAQEEZjBkMCQGCCsGAQUFBzABhhhodHRwOi8vb2Nz -cC52ZXJpc2lnbi5jb20wPAYIKwYBBQUHMAKGMGh0dHA6Ly9TVlJJbnRsLUczLWFp -YS52ZXJpc2lnbi5jb20vU1ZSSW50bEczLmNlcjBuBggrBgEFBQcBDARiMGChXqBc -MFowWDBWFglpbWFnZS9naWYwITAfMAcGBSsOAwIaBBRLa7kolgYMu9BSOJsprEsH -iyEFGDAmFiRodHRwOi8vbG9nby52ZXJpc2lnbi5jb20vdnNsb2dvMS5naWYwDQYJ -KoZIhvcNAQEFBQADggEBACQuPyIJqXwUyFRWw9x5yDXgMW4zYFopQYOw/ItRY522 -O5BsySTh56BWS6mQB07XVfxmYUGAvRQDA5QHpmY8jIlNwSmN3s8RKo+fAtiNRlcL -x/mWSfuMs3D/S6ev3D6+dpEMZtjrhOdctsarMKp8n/hPbwhAbg5hVjpkW5n8vz2y -0KxvvkA1AxpLwpVv7OlK17ttzIHw8bp9HTlHBU5s8bKz4a565V/a5HI0CSEv/+0y -ko4/ghTnZc1CkmUngKKeFMSah/mT/xAh8XnE2l1AazFa8UKuYki1e+ArHaGZc4ix -UYOtiRphwfuYQhRZ7qX9q2MMkCMI65XNK/SaFrAbbG0= ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/nullbytecert.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/nullbytecert.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/nullbytecert.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/nullbytecert.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -Certificate: - Data: - Version: 3 (0x2) - Serial Number: 0 (0x0) - Signature Algorithm: sha1WithRSAEncryption - Issuer: C=US, ST=Oregon, L=Beaverton, O=Python Software Foundation, OU=Python Core Development, CN=null.python.org\x00example.org/emailAddress=python-dev@python.org - Validity - Not Before: Aug 7 13:11:52 2013 GMT - Not After : Aug 7 13:12:52 2013 GMT - Subject: C=US, ST=Oregon, L=Beaverton, O=Python Software Foundation, OU=Python Core Development, CN=null.python.org\x00example.org/emailAddress=python-dev@python.org - Subject Public Key Info: - Public Key Algorithm: rsaEncryption - Public-Key: (2048 bit) - Modulus: - 00:b5:ea:ed:c9:fb:46:7d:6f:3b:76:80:dd:3a:f3: - 03:94:0b:a7:a6:db:ec:1d:df:ff:23:74:08:9d:97: - 16:3f:a3:a4:7b:3e:1b:0e:96:59:25:03:a7:26:e2: - 88:a9:cf:79:cd:f7:04:56:b0:ab:79:32:6e:59:c1: - 32:30:54:eb:58:a8:cb:91:f0:42:a5:64:27:cb:d4: - 56:31:88:52:ad:cf:bd:7f:f0:06:64:1f:cc:27:b8: - a3:8b:8c:f3:d8:29:1f:25:0b:f5:46:06:1b:ca:02: - 45:ad:7b:76:0a:9c:bf:bb:b9:ae:0d:16:ab:60:75: - ae:06:3e:9c:7c:31:dc:92:2f:29:1a:e0:4b:0c:91: - 90:6c:e9:37:c5:90:d7:2a:d7:97:15:a3:80:8f:5d: - 7b:49:8f:54:30:d4:97:2c:1c:5b:37:b5:ab:69:30: - 68:43:d3:33:78:4b:02:60:f5:3c:44:80:a1:8f:e7: - f0:0f:d1:5e:87:9e:46:cf:62:fc:f9:bf:0c:65:12: - f1:93:c8:35:79:3f:c8:ec:ec:47:f5:ef:be:44:d5: - ae:82:1e:2d:9a:9f:98:5a:67:65:e1:74:70:7c:cb: - d3:c2:ce:0e:45:49:27:dc:e3:2d:d4:fb:48:0e:2f: - 9e:77:b8:14:46:c0:c4:36:ca:02:ae:6a:91:8c:da: - 2f:85 - Exponent: 65537 (0x10001) - X509v3 extensions: - X509v3 Basic Constraints: critical - CA:FALSE - X509v3 Subject Key Identifier: - 88:5A:55:C0:52:FF:61:CD:52:A3:35:0F:EA:5A:9C:24:38:22:F7:5C - X509v3 Key Usage: - Digital Signature, Non Repudiation, Key Encipherment - X509v3 Subject Alternative Name: - ************************************************************* - WARNING: The values for DNS, email and URI are WRONG. OpenSSL - doesn't print the text after a NULL byte. - ************************************************************* - DNS:altnull.python.org, email:null@python.org, URI:http://null.python.org, IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1 - Signature Algorithm: sha1WithRSAEncryption - ac:4f:45:ef:7d:49:a8:21:70:8e:88:59:3e:d4:36:42:70:f5: - a3:bd:8b:d7:a8:d0:58:f6:31:4a:b1:a4:a6:dd:6f:d9:e8:44: - 3c:b6:0a:71:d6:7f:b1:08:61:9d:60:ce:75:cf:77:0c:d2:37: - 86:02:8d:5e:5d:f9:0f:71:b4:16:a8:c1:3d:23:1c:f1:11:b3: - 56:6e:ca:d0:8d:34:94:e6:87:2a:99:f2:ae:ae:cc:c2:e8:86: - de:08:a8:7f:c5:05:fa:6f:81:a7:82:e6:d0:53:9d:34:f4:ac: - 3e:40:fe:89:57:7a:29:a4:91:7e:0b:c6:51:31:e5:10:2f:a4: - 60:76:cd:95:51:1a:be:8b:a1:b0:fd:ad:52:bd:d7:1b:87:60: - d2:31:c7:17:c4:18:4f:2d:08:25:a3:a7:4f:b7:92:ca:e2:f5: - 25:f1:54:75:81:9d:b3:3d:61:a2:f7:da:ed:e1:c6:6f:2c:60: - 1f:d8:6f:c5:92:05:ab:c9:09:62:49:a9:14:ad:55:11:cc:d6: - 4a:19:94:99:97:37:1d:81:5f:8b:cf:a3:a8:96:44:51:08:3d: - 0b:05:65:12:eb:b6:70:80:88:48:72:4f:c6:c2:da:cf:cd:8e: - 5b:ba:97:2f:60:b4:96:56:49:5e:3a:43:76:63:04:be:2a:f6: - c1:ca:a9:94 ------BEGIN CERTIFICATE----- -MIIE2DCCA8CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBxTELMAkGA1UEBhMCVVMx -DzANBgNVBAgMBk9yZWdvbjESMBAGA1UEBwwJQmVhdmVydG9uMSMwIQYDVQQKDBpQ -eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEgMB4GA1UECwwXUHl0aG9uIENvcmUg -RGV2ZWxvcG1lbnQxJDAiBgNVBAMMG251bGwucHl0aG9uLm9yZwBleGFtcGxlLm9y -ZzEkMCIGCSqGSIb3DQEJARYVcHl0aG9uLWRldkBweXRob24ub3JnMB4XDTEzMDgw -NzEzMTE1MloXDTEzMDgwNzEzMTI1MlowgcUxCzAJBgNVBAYTAlVTMQ8wDQYDVQQI -DAZPcmVnb24xEjAQBgNVBAcMCUJlYXZlcnRvbjEjMCEGA1UECgwaUHl0aG9uIFNv -ZnR3YXJlIEZvdW5kYXRpb24xIDAeBgNVBAsMF1B5dGhvbiBDb3JlIERldmVsb3Bt -ZW50MSQwIgYDVQQDDBtudWxsLnB5dGhvbi5vcmcAZXhhbXBsZS5vcmcxJDAiBgkq -hkiG9w0BCQEWFXB5dGhvbi1kZXZAcHl0aG9uLm9yZzCCASIwDQYJKoZIhvcNAQEB -BQADggEPADCCAQoCggEBALXq7cn7Rn1vO3aA3TrzA5QLp6bb7B3f/yN0CJ2XFj+j -pHs+Gw6WWSUDpybiiKnPec33BFawq3kyblnBMjBU61ioy5HwQqVkJ8vUVjGIUq3P -vX/wBmQfzCe4o4uM89gpHyUL9UYGG8oCRa17dgqcv7u5rg0Wq2B1rgY+nHwx3JIv -KRrgSwyRkGzpN8WQ1yrXlxWjgI9de0mPVDDUlywcWze1q2kwaEPTM3hLAmD1PESA -oY/n8A/RXoeeRs9i/Pm/DGUS8ZPINXk/yOzsR/XvvkTVroIeLZqfmFpnZeF0cHzL -08LODkVJJ9zjLdT7SA4vnne4FEbAxDbKAq5qkYzaL4UCAwEAAaOB0DCBzTAMBgNV -HRMBAf8EAjAAMB0GA1UdDgQWBBSIWlXAUv9hzVKjNQ/qWpwkOCL3XDALBgNVHQ8E -BAMCBeAwgZAGA1UdEQSBiDCBhYIeYWx0bnVsbC5weXRob24ub3JnAGV4YW1wbGUu -Y29tgSBudWxsQHB5dGhvbi5vcmcAdXNlckBleGFtcGxlLm9yZ4YpaHR0cDovL251 -bGwucHl0aG9uLm9yZwBodHRwOi8vZXhhbXBsZS5vcmeHBMAAAgGHECABDbgAAAAA -AAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAKxPRe99SaghcI6IWT7UNkJw9aO9 -i9eo0Fj2MUqxpKbdb9noRDy2CnHWf7EIYZ1gznXPdwzSN4YCjV5d+Q9xtBaowT0j -HPERs1ZuytCNNJTmhyqZ8q6uzMLoht4IqH/FBfpvgaeC5tBTnTT0rD5A/olXeimk -kX4LxlEx5RAvpGB2zZVRGr6LobD9rVK91xuHYNIxxxfEGE8tCCWjp0+3ksri9SXx -VHWBnbM9YaL32u3hxm8sYB/Yb8WSBavJCWJJqRStVRHM1koZlJmXNx2BX4vPo6iW -RFEIPQsFZRLrtnCAiEhyT8bC2s/Njlu6ly9gtJZWSV46Q3ZjBL4q9sHKqZQ= ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/pystone.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/pystone.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/pystone.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/pystone.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,272 +0,0 @@ -#!/usr/bin/env python3 - -""" -"PYSTONE" Benchmark Program - -Version: Python/1.1 (corresponds to C/1.1 plus 2 Pystone fixes) - -Author: Reinhold P. Weicker, CACM Vol 27, No 10, 10/84 pg. 1013. - - Translated from ADA to C by Rick Richardson. - Every method to preserve ADA-likeness has been used, - at the expense of C-ness. - - Translated from C to Python by Guido van Rossum. - -Version History: - - Version 1.1 corrects two bugs in version 1.0: - - First, it leaked memory: in Proc1(), NextRecord ends - up having a pointer to itself. I have corrected this - by zapping NextRecord.PtrComp at the end of Proc1(). - - Second, Proc3() used the operator != to compare a - record to None. This is rather inefficient and not - true to the intention of the original benchmark (where - a pointer comparison to None is intended; the != - operator attempts to find a method __cmp__ to do value - comparison of the record). Version 1.1 runs 5-10 - percent faster than version 1.0, so benchmark figures - of different versions can't be compared directly. - -""" - -from __future__ import print_function - -from time import clock - -LOOPS = 50000 - -__version__ = "1.1" - -[Ident1, Ident2, Ident3, Ident4, Ident5] = range(1, 6) - -class Record(object): - - def __init__(self, PtrComp = None, Discr = 0, EnumComp = 0, - IntComp = 0, StringComp = 0): - self.PtrComp = PtrComp - self.Discr = Discr - self.EnumComp = EnumComp - self.IntComp = IntComp - self.StringComp = StringComp - - def copy(self): - return Record(self.PtrComp, self.Discr, self.EnumComp, - self.IntComp, self.StringComp) - -TRUE = 1 -FALSE = 0 - -def main(loops=LOOPS): - benchtime, stones = pystones(loops) - print("Pystone(%s) time for %d passes = %g" % \ - (__version__, loops, benchtime)) - print("This machine benchmarks at %g pystones/second" % stones) - - -def pystones(loops=LOOPS): - return Proc0(loops) - -IntGlob = 0 -BoolGlob = FALSE -Char1Glob = '\0' -Char2Glob = '\0' -Array1Glob = [0]*51 -Array2Glob = [x[:] for x in [Array1Glob]*51] -PtrGlb = None -PtrGlbNext = None - -def Proc0(loops=LOOPS): - global IntGlob - global BoolGlob - global Char1Glob - global Char2Glob - global Array1Glob - global Array2Glob - global PtrGlb - global PtrGlbNext - - starttime = clock() - for i in range(loops): - pass - nulltime = clock() - starttime - - PtrGlbNext = Record() - PtrGlb = Record() - PtrGlb.PtrComp = PtrGlbNext - PtrGlb.Discr = Ident1 - PtrGlb.EnumComp = Ident3 - PtrGlb.IntComp = 40 - PtrGlb.StringComp = "DHRYSTONE PROGRAM, SOME STRING" - String1Loc = "DHRYSTONE PROGRAM, 1'ST STRING" - Array2Glob[8][7] = 10 - - starttime = clock() - - for i in range(loops): - Proc5() - Proc4() - IntLoc1 = 2 - IntLoc2 = 3 - String2Loc = "DHRYSTONE PROGRAM, 2'ND STRING" - EnumLoc = Ident2 - BoolGlob = not Func2(String1Loc, String2Loc) - while IntLoc1 < IntLoc2: - IntLoc3 = 5 * IntLoc1 - IntLoc2 - IntLoc3 = Proc7(IntLoc1, IntLoc2) - IntLoc1 = IntLoc1 + 1 - Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3) - PtrGlb = Proc1(PtrGlb) - CharIndex = 'A' - while CharIndex <= Char2Glob: - if EnumLoc == Func1(CharIndex, 'C'): - EnumLoc = Proc6(Ident1) - CharIndex = chr(ord(CharIndex)+1) - IntLoc3 = IntLoc2 * IntLoc1 - IntLoc2 = IntLoc3 / IntLoc1 - IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1 - IntLoc1 = Proc2(IntLoc1) - - benchtime = clock() - starttime - nulltime - if benchtime == 0.0: - loopsPerBenchtime = 0.0 - else: - loopsPerBenchtime = (loops / benchtime) - return benchtime, loopsPerBenchtime - -def Proc1(PtrParIn): - PtrParIn.PtrComp = NextRecord = PtrGlb.copy() - PtrParIn.IntComp = 5 - NextRecord.IntComp = PtrParIn.IntComp - NextRecord.PtrComp = PtrParIn.PtrComp - NextRecord.PtrComp = Proc3(NextRecord.PtrComp) - if NextRecord.Discr == Ident1: - NextRecord.IntComp = 6 - NextRecord.EnumComp = Proc6(PtrParIn.EnumComp) - NextRecord.PtrComp = PtrGlb.PtrComp - NextRecord.IntComp = Proc7(NextRecord.IntComp, 10) - else: - PtrParIn = NextRecord.copy() - NextRecord.PtrComp = None - return PtrParIn - -def Proc2(IntParIO): - IntLoc = IntParIO + 10 - while 1: - if Char1Glob == 'A': - IntLoc = IntLoc - 1 - IntParIO = IntLoc - IntGlob - EnumLoc = Ident1 - if EnumLoc == Ident1: - break - return IntParIO - -def Proc3(PtrParOut): - global IntGlob - - if PtrGlb is not None: - PtrParOut = PtrGlb.PtrComp - else: - IntGlob = 100 - PtrGlb.IntComp = Proc7(10, IntGlob) - return PtrParOut - -def Proc4(): - global Char2Glob - - BoolLoc = Char1Glob == 'A' - BoolLoc = BoolLoc or BoolGlob - Char2Glob = 'B' - -def Proc5(): - global Char1Glob - global BoolGlob - - Char1Glob = 'A' - BoolGlob = FALSE - -def Proc6(EnumParIn): - EnumParOut = EnumParIn - if not Func3(EnumParIn): - EnumParOut = Ident4 - if EnumParIn == Ident1: - EnumParOut = Ident1 - elif EnumParIn == Ident2: - if IntGlob > 100: - EnumParOut = Ident1 - else: - EnumParOut = Ident4 - elif EnumParIn == Ident3: - EnumParOut = Ident2 - elif EnumParIn == Ident4: - pass - elif EnumParIn == Ident5: - EnumParOut = Ident3 - return EnumParOut - -def Proc7(IntParI1, IntParI2): - IntLoc = IntParI1 + 2 - IntParOut = IntParI2 + IntLoc - return IntParOut - -def Proc8(Array1Par, Array2Par, IntParI1, IntParI2): - global IntGlob - - IntLoc = IntParI1 + 5 - Array1Par[IntLoc] = IntParI2 - Array1Par[IntLoc+1] = Array1Par[IntLoc] - Array1Par[IntLoc+30] = IntLoc - for IntIndex in range(IntLoc, IntLoc+2): - Array2Par[IntLoc][IntIndex] = IntLoc - Array2Par[IntLoc][IntLoc-1] = Array2Par[IntLoc][IntLoc-1] + 1 - Array2Par[IntLoc+20][IntLoc] = Array1Par[IntLoc] - IntGlob = 5 - -def Func1(CharPar1, CharPar2): - CharLoc1 = CharPar1 - CharLoc2 = CharLoc1 - if CharLoc2 != CharPar2: - return Ident1 - else: - return Ident2 - -def Func2(StrParI1, StrParI2): - IntLoc = 1 - while IntLoc <= 1: - if Func1(StrParI1[IntLoc], StrParI2[IntLoc+1]) == Ident1: - CharLoc = 'A' - IntLoc = IntLoc + 1 - if CharLoc >= 'W' and CharLoc <= 'Z': - IntLoc = 7 - if CharLoc == 'X': - return TRUE - else: - if StrParI1 > StrParI2: - IntLoc = IntLoc + 7 - return TRUE - else: - return FALSE - -def Func3(EnumParIn): - EnumLoc = EnumParIn - if EnumLoc == Ident3: return TRUE - return FALSE - -if __name__ == '__main__': - import sys - def error(msg): - print(msg, end=' ', file=sys.stderr) - print("usage: %s [number_of_loops]" % sys.argv[0], file=sys.stderr) - sys.exit(100) - nargs = len(sys.argv) - 1 - if nargs > 1: - error("%d arguments are too many;" % nargs) - elif nargs == 1: - try: loops = int(sys.argv[1]) - except ValueError: - error("Invalid argument %r;" % sys.argv[1]) - else: - loops = LOOPS - main(loops) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/sha256.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/sha256.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/sha256.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/sha256.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -# Certificate chain for https://sha256.tbs-internet.com - 0 s:/C=FR/postalCode=14000/ST=Calvados/L=CAEN/street=22 rue de Bretagne/O=TBS INTERNET/OU=0002 440443810/OU=sha-256 production/CN=sha256.tbs-internet.com - i:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC ------BEGIN CERTIFICATE----- -MIIGXDCCBUSgAwIBAgIRAKpVmHgg9nfCodAVwcP4siwwDQYJKoZIhvcNAQELBQAw -gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl -bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u -ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv -cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg -Q0EgU0dDMB4XDTEyMDEwNDAwMDAwMFoXDTE0MDIxNzIzNTk1OVowgcsxCzAJBgNV -BAYTAkZSMQ4wDAYDVQQREwUxNDAwMDERMA8GA1UECBMIQ2FsdmFkb3MxDTALBgNV -BAcTBENBRU4xGzAZBgNVBAkTEjIyIHJ1ZSBkZSBCcmV0YWduZTEVMBMGA1UEChMM -VEJTIElOVEVSTkVUMRcwFQYDVQQLEw4wMDAyIDQ0MDQ0MzgxMDEbMBkGA1UECxMS -c2hhLTI1NiBwcm9kdWN0aW9uMSAwHgYDVQQDExdzaGEyNTYudGJzLWludGVybmV0 -LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKQIX/zdJcyxty0m -PM1XQSoSSifueS3AVcgqMsaIKS/u+rYzsv4hQ/qA6vLn5m5/ewUcZDj7zdi6rBVf -PaVNXJ6YinLX0tkaW8TEjeVuZG5yksGZlhCt1CJ1Ho9XLiLaP4uJ7MCoNUntpJ+E -LfrOdgsIj91kPmwjDJeztVcQCvKzhjVJA/KxdInc0JvOATn7rpaSmQI5bvIjufgo -qVsTPwVFzuUYULXBk7KxRT7MiEqnd5HvviNh0285QC478zl3v0I0Fb5El4yD3p49 -IthcRnxzMKc0UhU5ogi0SbONyBfm/mzONVfSxpM+MlyvZmJqrbuuLoEDzJD+t8PU -xSuzgbcCAwEAAaOCAj4wggI6MB8GA1UdIwQYMBaAFAdEdoWTKLx/bXjSCuv6TEvf -2YIfMB0GA1UdDgQWBBT/qTGYdaj+f61c2IRFL/B1eEsM8DAOBgNVHQ8BAf8EBAMC -BaAwDAYDVR0TAQH/BAIwADA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIG -CisGAQQBgjcKAwMGCWCGSAGG+EIEATBLBgNVHSAERDBCMEAGCisGAQQB5TcCBAEw -MjAwBggrBgEFBQcCARYkaHR0cHM6Ly93d3cudGJzLWludGVybmV0LmNvbS9DQS9D -UFM0MG0GA1UdHwRmMGQwMqAwoC6GLGh0dHA6Ly9jcmwudGJzLWludGVybmV0LmNv -bS9UQlNYNTA5Q0FTR0MuY3JsMC6gLKAqhihodHRwOi8vY3JsLnRicy14NTA5LmNv -bS9UQlNYNTA5Q0FTR0MuY3JsMIGmBggrBgEFBQcBAQSBmTCBljA4BggrBgEFBQcw -AoYsaHR0cDovL2NydC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQVNHQy5jcnQw -NAYIKwYBBQUHMAKGKGh0dHA6Ly9jcnQudGJzLXg1MDkuY29tL1RCU1g1MDlDQVNH -Qy5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnRicy14NTA5LmNvbTA/BgNV -HREEODA2ghdzaGEyNTYudGJzLWludGVybmV0LmNvbYIbd3d3LnNoYTI1Ni50YnMt -aW50ZXJuZXQuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA0pOuL8QvAa5yksTbGShzX -ABApagunUGoEydv4YJT1MXy9tTp7DrWaozZSlsqBxrYAXP1d9r2fuKbEniYHxaQ0 -UYaf1VSIlDo1yuC8wE7wxbHDIpQ/E5KAyxiaJ8obtDhFstWAPAH+UoGXq0kj2teN -21sFQ5dXgA95nldvVFsFhrRUNB6xXAcaj0VZFhttI0ZfQZmQwEI/P+N9Jr40OGun -aa+Dn0TMeUH4U20YntfLbu2nDcJcYfyurm+8/0Tr4HznLnedXu9pCPYj0TaddrgT -XO0oFiyy7qGaY6+qKh71yD64Y3ycCJ/HR9Wm39mjZYc9ezYwT4noP6r7Lk8YO7/q ------END CERTIFICATE----- - 1 s:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC - i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root ------BEGIN CERTIFICATE----- -MIIFVjCCBD6gAwIBAgIQXpDZ0ETJMV02WTx3GTnhhTANBgkqhkiG9w0BAQUFADBv -MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk -ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF -eHRlcm5hbCBDQSBSb290MB4XDTA1MTIwMTAwMDAwMFoXDTE5MDYyNDE5MDYzMFow -gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl -bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u -ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv -cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg -Q0EgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgOkO3f7wzN6 -rOjg45tR5vjBfzK7qmV9IBxb/QW9EEXxG+E7FNhZqQLtwGBKoSsHTnQqV75wWMk0 -9tinWvftBkSpj5sTi/8cbzJfUvTSVYh3Qxv6AVVjMMH/ruLjE6y+4PoaPs8WoYAQ -ts5R4Z1g8c/WnTepLst2x0/Wv7GmuoQi+gXvHU6YrBiu7XkeYhzc95QdviWSJRDk -owhb5K43qhcvjRmBfO/paGlCliDGZp8mHwrI21mwobWpVjTxZRwYO3bd4+TGcI4G -Ie5wmHwE8F7SK1tgSqbBacKjDa93j7txKkfz/Yd2n7TGqOXiHPsJpG655vrKtnXk -9vs1zoDeJQIDAQABo4IBljCCAZIwHQYDVR0OBBYEFAdEdoWTKLx/bXjSCuv6TEvf -2YIfMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMCAGA1UdJQQZ -MBcGCisGAQQBgjcKAwMGCWCGSAGG+EIEATAYBgNVHSAEETAPMA0GCysGAQQBgOU3 -AgQBMHsGA1UdHwR0MHIwOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0Fk -ZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMDagNKAyhjBodHRwOi8vY3JsLmNvbW9k -by5uZXQvQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5jcmwwgYAGCCsGAQUFBwEBBHQw -cjA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21vZG9jYS5jb20vQWRkVHJ1c3RV -VE5TR0NDQS5jcnQwNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvLm5ldC9B -ZGRUcnVzdFVUTlNHQ0NBLmNydDARBglghkgBhvhCAQEEBAMCAgQwDQYJKoZIhvcN -AQEFBQADggEBAK2zEzs+jcIrVK9oDkdDZNvhuBYTdCfpxfFs+OAujW0bIfJAy232 -euVsnJm6u/+OrqKudD2tad2BbejLLXhMZViaCmK7D9nrXHx4te5EP8rL19SUVqLY -1pTnv5dhNgEgvA7n5lIzDSYs7yRLsr7HJsYPr6SeYSuZizyX1SNz7ooJ32/F3X98 -RB0Mlc/E0OyOrkQ9/y5IrnpnaSora8CnUrV5XNOg+kyCz9edCyx4D5wXYcwZPVWz -8aDqquESrezPyjtfi4WRO4s/VD3HLZvOxzMrWAVYCDG9FxaOhF0QGuuG1F7F3GKV -v6prNyCl016kRl2j1UT+a7gLd8fA25A4C9E= ------END CERTIFICATE----- - 2 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root - i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC ------BEGIN CERTIFICATE----- -MIIEZjCCA06gAwIBAgIQUSYKkxzif5zDpV954HKugjANBgkqhkiG9w0BAQUFADCB -kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw -IFNHQzAeFw0wNTA2MDcwODA5MTBaFw0xOTA2MjQxOTA2MzBaMG8xCzAJBgNVBAYT -AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0 -ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB -IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC39xoz5vIABC05 -4E5b7R+8bA/Ntfojts7emxEzl6QpTH2Tn71KvJPtAxrjj8/lbVBa1pcplFqAsEl6 -2y6V/bjKvzc4LR4+kUGtcFbH8E8/6DKedMrIkFTpxl8PeJ2aQDwOrGGqXhSPnoeh -alDc15pOrwWzpnGUnHGzUGAKxxOdOAeGAqjpqGkmGJCrTLBPI6s6T4TY386f4Wlv -u9dC12tE5Met7m1BX3JacQg3s3llpFmglDf3AC8NwpJy2tA4ctsUqEXEXSp9t7TW -xO6szRNEt8kr3UMAJfphuWlqWCMRt6czj1Z1WfXNKddGtworZbbTQm8Vsrh7++/p -XVPVNFonAgMBAAGjgdgwgdUwHwYDVR0jBBgwFoAUUzLRs89/+uDxoF2FTpLSnkUd -tE8wHQYDVR0OBBYEFK29mHo0tCb3+sQmVO8DveAky1QaMA4GA1UdDwEB/wQEAwIB -BjAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBAjAgBgNVHSUEGTAX -BgorBgEEAYI3CgMDBglghkgBhvhCBAEwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDov -L2NybC51c2VydHJ1c3QuY29tL1VUTi1EQVRBQ29ycFNHQy5jcmwwDQYJKoZIhvcN -AQEFBQADggEBAMbuUxdoFLJRIh6QWA2U/b3xcOWGLcM2MY9USEbnLQg3vGwKYOEO -rVE04BKT6b64q7gmtOmWPSiPrmQH/uAB7MXjkesYoPF1ftsK5p+R26+udd8jkWjd -FwBaS/9kbHDrARrQkNnHptZt9hPk/7XJ0h4qy7ElQyZ42TCbTg0evmnv3+r+LbPM -+bDdtRTKkdSytaX7ARmjR3mfnYyVhzT4HziS2jamEfpr62vp3EV4FTkG101B5CHI -3C+H0be/SGB1pWLLJN47YaApIKa+xWycxOkKaSLvkTr6Jq/RW0GnOuL4OAdCq8Fb -+M5tug8EPzI0rNwEKNdwMBQmBsTkm5jVz3g= ------END CERTIFICATE----- - 3 s:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC - i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC ------BEGIN CERTIFICATE----- -MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB -kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw -IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG -EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD -VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu -dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN -BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 -E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ -D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK -4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq -lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW -bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB -o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT -MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js -LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr -BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB -AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft -Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj -j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH -KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv -2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 -mfnGV/TJVTl4uix5yaaIK/QI ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_cert.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_cert.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_cert.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_cert.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV -BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u -IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw -MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH -Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k -YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw -gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7 -6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt -pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw -FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd -BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G -lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1 -CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX ------END CERTIFICATE----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_key.passwd.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_key.passwd.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_key.passwd.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_key.passwd.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,1A8D9D2A02EC698A - -kJYbfZ8L0sfe9Oty3gw0aloNnY5E8fegRfQLZlNoxTl6jNt0nIwI8kDJ36CZgR9c -u3FDJm/KqrfUoz8vW+qEnWhSG7QPX2wWGPHd4K94Yz/FgrRzZ0DoK7XxXq9gOtVA -AVGQhnz32p+6WhfGsCr9ArXEwRZrTk/FvzEPaU5fHcoSkrNVAGX8IpSVkSDwEDQr -Gv17+cfk99UV1OCza6yKHoFkTtrC+PZU71LomBabivS2Oc4B9hYuSR2hF01wTHP+ -YlWNagZOOVtNz4oKK9x9eNQpmfQXQvPPTfusexKIbKfZrMvJoxcm1gfcZ0H/wK6P -6wmXSG35qMOOztCZNtperjs1wzEBXznyK8QmLcAJBjkfarABJX9vBEzZV0OUKhy+ -noORFwHTllphbmydLhu6ehLUZMHPhzAS5UN7srtpSN81eerDMy0RMUAwA7/PofX1 -94Me85Q8jP0PC9ETdsJcPqLzAPETEYu0ELewKRcrdyWi+tlLFrpE5KT/s5ecbl9l -7B61U4Kfd1PIXc/siINhU3A3bYK+845YyUArUOnKf1kEox7p1RpD7yFqVT04lRTo -cibNKATBusXSuBrp2G6GNuhWEOSafWCKJQAzgCYIp6ZTV2khhMUGppc/2H3CF6cO -zX0KtlPVZC7hLkB6HT8SxYUwF1zqWY7+/XPPdc37MeEZ87Q3UuZwqORLY+Z0hpgt -L5JXBCoklZhCAaN2GqwFLXtGiRSRFGY7xXIhbDTlE65Wv1WGGgDLMKGE1gOz3yAo -2jjG1+yAHJUdE69XTFHSqSkvaloA1W03LdMXZ9VuQJ/ySXCie6ABAQ== ------END RSA PRIVATE KEY----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_key.pem pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_key.pem --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_key.pem 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_key.pem 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANtb0+YrKuxevGpm -LrjaUhZSgz6zFAmuGFmKmUbdjmfv9zSmmdsQIksK++jK0Be9LeZy20j6ahOfuVa0 -ufEmPoP7Fy4hXegKZR9cCWcIe/A6H2xWF1IIJLRTLaU8ol/I7T+um5HD5AwAwNPP -USNU0Eegmvp+xxWu3NX2m1Veot85AgMBAAECgYA3ZdZ673X0oexFlq7AAmrutkHt -CL7LvwrpOiaBjhyTxTeSNWzvtQBkIU8DOI0bIazA4UreAFffwtvEuPmonDb3F+Iq -SMAu42XcGyVZEl+gHlTPU9XRX7nTOXVt+MlRRRxL6t9GkGfUAXI3XxJDXW3c0vBK -UL9xqD8cORXOfE06rQJBAP8mEX1ERkR64Ptsoe4281vjTlNfIbs7NMPkUnrn9N/Y -BLhjNIfQ3HFZG8BTMLfX7kCS9D593DW5tV4Z9BP/c6cCQQDcFzCcVArNh2JSywOQ -ZfTfRbJg/Z5Lt9Fkngv1meeGNPgIMLN8Sg679pAOOWmzdMO3V706rNPzSVMME7E5 -oPIfAkEA8pDddarP5tCvTTgUpmTFbakm0KoTZm2+FzHcnA4jRh+XNTjTOv98Y6Ik -eO5d1ZnKXseWvkZncQgxfdnMqqpj5wJAcNq/RVne1DbYlwWchT2Si65MYmmJ8t+F -0mcsULqjOnEMwf5e+ptq5LzwbyrHZYq5FNk7ocufPv/ZQrcSSC+cFwJBAKvOJByS -x56qyGeZLOQlWS2JS3KJo59XuLFGqcbgN9Om9xFa41Yb4N9NvplFivsvZdw3m1Q/ -SPIXQuT8RMPDVNQ= ------END PRIVATE KEY----- diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_servers.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_servers.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/ssl_servers.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/ssl_servers.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,207 +0,0 @@ -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import filter, str -from future import utils -import os -import sys -import ssl -import pprint -import socket -from future.backports.urllib import parse as urllib_parse -from future.backports.http.server import (HTTPServer as _HTTPServer, - SimpleHTTPRequestHandler, BaseHTTPRequestHandler) -from future.backports.test import support -threading = support.import_module("threading") - -here = os.path.dirname(__file__) - -HOST = support.HOST -CERTFILE = os.path.join(here, 'keycert.pem') - -# This one's based on HTTPServer, which is based on SocketServer - -class HTTPSServer(_HTTPServer): - - def __init__(self, server_address, handler_class, context): - _HTTPServer.__init__(self, server_address, handler_class) - self.context = context - - def __str__(self): - return ('<%s %s:%s>' % - (self.__class__.__name__, - self.server_name, - self.server_port)) - - def get_request(self): - # override this to wrap socket with SSL - try: - sock, addr = self.socket.accept() - sslconn = self.context.wrap_socket(sock, server_side=True) - except socket.error as e: - # socket errors are silenced by the caller, print them here - if support.verbose: - sys.stderr.write("Got an error:\n%s\n" % e) - raise - return sslconn, addr - -class RootedHTTPRequestHandler(SimpleHTTPRequestHandler): - # need to override translate_path to get a known root, - # instead of using os.curdir, since the test could be - # run from anywhere - - server_version = "TestHTTPS/1.0" - root = here - # Avoid hanging when a request gets interrupted by the client - timeout = 5 - - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - # abandon query parameters - path = urllib.parse.urlparse(path)[2] - path = os.path.normpath(urllib.parse.unquote(path)) - words = path.split('/') - words = filter(None, words) - path = self.root - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - path = os.path.join(path, word) - return path - - def log_message(self, format, *args): - # we override this to suppress logging unless "verbose" - if support.verbose: - sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" % - (self.server.server_address, - self.server.server_port, - self.request.cipher(), - self.log_date_time_string(), - format%args)) - - -class StatsRequestHandler(BaseHTTPRequestHandler): - """Example HTTP request handler which returns SSL statistics on GET - requests. - """ - - server_version = "StatsHTTPS/1.0" - - def do_GET(self, send_body=True): - """Serve a GET request.""" - sock = self.rfile.raw._sock - context = sock.context - stats = { - 'session_cache': context.session_stats(), - 'cipher': sock.cipher(), - 'compression': sock.compression(), - } - body = pprint.pformat(stats) - body = body.encode('utf-8') - self.send_response(200) - self.send_header("Content-type", "text/plain; charset=utf-8") - self.send_header("Content-Length", str(len(body))) - self.end_headers() - if send_body: - self.wfile.write(body) - - def do_HEAD(self): - """Serve a HEAD request.""" - self.do_GET(send_body=False) - - def log_request(self, format, *args): - if support.verbose: - BaseHTTPRequestHandler.log_request(self, format, *args) - - -class HTTPSServerThread(threading.Thread): - - def __init__(self, context, host=HOST, handler_class=None): - self.flag = None - self.server = HTTPSServer((host, 0), - handler_class or RootedHTTPRequestHandler, - context) - self.port = self.server.server_port - threading.Thread.__init__(self) - self.daemon = True - - def __str__(self): - return "<%s %s>" % (self.__class__.__name__, self.server) - - def start(self, flag=None): - self.flag = flag - threading.Thread.start(self) - - def run(self): - if self.flag: - self.flag.set() - try: - self.server.serve_forever(0.05) - finally: - self.server.server_close() - - def stop(self): - self.server.shutdown() - - -def make_https_server(case, certfile=CERTFILE, host=HOST, handler_class=None): - # we assume the certfile contains both private key and certificate - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.load_cert_chain(certfile) - server = HTTPSServerThread(context, host, handler_class) - flag = threading.Event() - server.start(flag) - flag.wait() - def cleanup(): - if support.verbose: - sys.stdout.write('stopping HTTPS server\n') - server.stop() - if support.verbose: - sys.stdout.write('joining HTTPS thread\n') - server.join() - case.addCleanup(cleanup) - return server - - -if __name__ == "__main__": - import argparse - parser = argparse.ArgumentParser( - description='Run a test HTTPS server. ' - 'By default, the current directory is served.') - parser.add_argument('-p', '--port', type=int, default=4433, - help='port to listen on (default: %(default)s)') - parser.add_argument('-q', '--quiet', dest='verbose', default=True, - action='store_false', help='be less verbose') - parser.add_argument('-s', '--stats', dest='use_stats_handler', default=False, - action='store_true', help='always return stats page') - parser.add_argument('--curve-name', dest='curve_name', type=str, - action='store', - help='curve name for EC-based Diffie-Hellman') - parser.add_argument('--dh', dest='dh_file', type=str, action='store', - help='PEM file containing DH parameters') - args = parser.parse_args() - - support.verbose = args.verbose - if args.use_stats_handler: - handler_class = StatsRequestHandler - else: - handler_class = RootedHTTPRequestHandler - if utils.PY2: - handler_class.root = os.getcwdu() - else: - handler_class.root = os.getcwd() - context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) - context.load_cert_chain(CERTFILE) - if args.curve_name: - context.set_ecdh_curve(args.curve_name) - if args.dh_file: - context.load_dh_params(args.dh_file) - - server = HTTPSServer(("", args.port), handler_class, context) - if args.verbose: - print("Listening on https://localhost:{0.port}".format(args)) - server.serve_forever(0.1) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/support.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/support.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/test/support.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/test/support.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2048 +0,0 @@ -# -*- coding: utf-8 -*- -"""Supporting definitions for the Python regression tests. - -Backported for python-future from Python 3.3 test/support.py. -""" - -from __future__ import (absolute_import, division, - print_function, unicode_literals) -from future import utils -from future.builtins import str, range, open, int, map, list - -import contextlib -import errno -import functools -import gc -import socket -import sys -import os -import platform -import shutil -import warnings -import unittest -# For Python 2.6 compatibility: -if not hasattr(unittest, 'skip'): - import unittest2 as unittest - -import importlib -# import collections.abc # not present on Py2.7 -import re -import subprocess -import imp -import time -try: - import sysconfig -except ImportError: - # sysconfig is not available on Python 2.6. Try using distutils.sysconfig instead: - from distutils import sysconfig -import fnmatch -import logging.handlers -import struct -import tempfile - -try: - if utils.PY3: - import _thread, threading - else: - import thread as _thread, threading -except ImportError: - _thread = None - threading = None -try: - import multiprocessing.process -except ImportError: - multiprocessing = None - -try: - import zlib -except ImportError: - zlib = None - -try: - import gzip -except ImportError: - gzip = None - -try: - import bz2 -except ImportError: - bz2 = None - -try: - import lzma -except ImportError: - lzma = None - -__all__ = [ - "Error", "TestFailed", "ResourceDenied", "import_module", "verbose", - "use_resources", "max_memuse", "record_original_stdout", - "get_original_stdout", "unload", "unlink", "rmtree", "forget", - "is_resource_enabled", "requires", "requires_freebsd_version", - "requires_linux_version", "requires_mac_ver", "find_unused_port", - "bind_port", "IPV6_ENABLED", "is_jython", "TESTFN", "HOST", "SAVEDCWD", - "temp_cwd", "findfile", "create_empty_file", "sortdict", - "check_syntax_error", "open_urlresource", "check_warnings", "CleanImport", - "EnvironmentVarGuard", "TransientResource", "captured_stdout", - "captured_stdin", "captured_stderr", "time_out", "socket_peer_reset", - "ioerror_peer_reset", "run_with_locale", 'temp_umask', - "transient_internet", "set_memlimit", "bigmemtest", "bigaddrspacetest", - "BasicTestRunner", "run_unittest", "run_doctest", "threading_setup", - "threading_cleanup", "reap_children", "cpython_only", "check_impl_detail", - "get_attribute", "swap_item", "swap_attr", "requires_IEEE_754", - "TestHandler", "Matcher", "can_symlink", "skip_unless_symlink", - "skip_unless_xattr", "import_fresh_module", "requires_zlib", - "PIPE_MAX_SIZE", "failfast", "anticipate_failure", "run_with_tz", - "requires_gzip", "requires_bz2", "requires_lzma", "suppress_crash_popup", - ] - -class Error(Exception): - """Base class for regression test exceptions.""" - -class TestFailed(Error): - """Test failed.""" - -class ResourceDenied(unittest.SkipTest): - """Test skipped because it requested a disallowed resource. - - This is raised when a test calls requires() for a resource that - has not be enabled. It is used to distinguish between expected - and unexpected skips. - """ - -@contextlib.contextmanager -def _ignore_deprecated_imports(ignore=True): - """Context manager to suppress package and module deprecation - warnings when importing them. - - If ignore is False, this context manager has no effect.""" - if ignore: - with warnings.catch_warnings(): - warnings.filterwarnings("ignore", ".+ (module|package)", - DeprecationWarning) - yield - else: - yield - - -def import_module(name, deprecated=False): - """Import and return the module to be tested, raising SkipTest if - it is not available. - - If deprecated is True, any module or package deprecation messages - will be suppressed.""" - with _ignore_deprecated_imports(deprecated): - try: - return importlib.import_module(name) - except ImportError as msg: - raise unittest.SkipTest(str(msg)) - - -def _save_and_remove_module(name, orig_modules): - """Helper function to save and remove a module from sys.modules - - Raise ImportError if the module can't be imported. - """ - # try to import the module and raise an error if it can't be imported - if name not in sys.modules: - __import__(name) - del sys.modules[name] - for modname in list(sys.modules): - if modname == name or modname.startswith(name + '.'): - orig_modules[modname] = sys.modules[modname] - del sys.modules[modname] - -def _save_and_block_module(name, orig_modules): - """Helper function to save and block a module in sys.modules - - Return True if the module was in sys.modules, False otherwise. - """ - saved = True - try: - orig_modules[name] = sys.modules[name] - except KeyError: - saved = False - sys.modules[name] = None - return saved - - -def anticipate_failure(condition): - """Decorator to mark a test that is known to be broken in some cases - - Any use of this decorator should have a comment identifying the - associated tracker issue. - """ - if condition: - return unittest.expectedFailure - return lambda f: f - - -def import_fresh_module(name, fresh=(), blocked=(), deprecated=False): - """Import and return a module, deliberately bypassing sys.modules. - This function imports and returns a fresh copy of the named Python module - by removing the named module from sys.modules before doing the import. - Note that unlike reload, the original module is not affected by - this operation. - - *fresh* is an iterable of additional module names that are also removed - from the sys.modules cache before doing the import. - - *blocked* is an iterable of module names that are replaced with None - in the module cache during the import to ensure that attempts to import - them raise ImportError. - - The named module and any modules named in the *fresh* and *blocked* - parameters are saved before starting the import and then reinserted into - sys.modules when the fresh import is complete. - - Module and package deprecation messages are suppressed during this import - if *deprecated* is True. - - This function will raise ImportError if the named module cannot be - imported. - - If deprecated is True, any module or package deprecation messages - will be suppressed. - """ - # NOTE: test_heapq, test_json and test_warnings include extra sanity checks - # to make sure that this utility function is working as expected - with _ignore_deprecated_imports(deprecated): - # Keep track of modules saved for later restoration as well - # as those which just need a blocking entry removed - orig_modules = {} - names_to_remove = [] - _save_and_remove_module(name, orig_modules) - try: - for fresh_name in fresh: - _save_and_remove_module(fresh_name, orig_modules) - for blocked_name in blocked: - if not _save_and_block_module(blocked_name, orig_modules): - names_to_remove.append(blocked_name) - fresh_module = importlib.import_module(name) - except ImportError: - fresh_module = None - finally: - for orig_name, module in orig_modules.items(): - sys.modules[orig_name] = module - for name_to_remove in names_to_remove: - del sys.modules[name_to_remove] - return fresh_module - - -def get_attribute(obj, name): - """Get an attribute, raising SkipTest if AttributeError is raised.""" - try: - attribute = getattr(obj, name) - except AttributeError: - raise unittest.SkipTest("object %r has no attribute %r" % (obj, name)) - else: - return attribute - -verbose = 1 # Flag set to 0 by regrtest.py -use_resources = None # Flag set to [] by regrtest.py -max_memuse = 0 # Disable bigmem tests (they will still be run with - # small sizes, to make sure they work.) -real_max_memuse = 0 -failfast = False -match_tests = None - -# _original_stdout is meant to hold stdout at the time regrtest began. -# This may be "the real" stdout, or IDLE's emulation of stdout, or whatever. -# The point is to have some flavor of stdout the user can actually see. -_original_stdout = None -def record_original_stdout(stdout): - global _original_stdout - _original_stdout = stdout - -def get_original_stdout(): - return _original_stdout or sys.stdout - -def unload(name): - try: - del sys.modules[name] - except KeyError: - pass - -if sys.platform.startswith("win"): - def _waitfor(func, pathname, waitall=False): - # Perform the operation - func(pathname) - # Now setup the wait loop - if waitall: - dirname = pathname - else: - dirname, name = os.path.split(pathname) - dirname = dirname or '.' - # Check for `pathname` to be removed from the filesystem. - # The exponential backoff of the timeout amounts to a total - # of ~1 second after which the deletion is probably an error - # anyway. - # Testing on a i7@4.3GHz shows that usually only 1 iteration is - # required when contention occurs. - timeout = 0.001 - while timeout < 1.0: - # Note we are only testing for the existence of the file(s) in - # the contents of the directory regardless of any security or - # access rights. If we have made it this far, we have sufficient - # permissions to do that much using Python's equivalent of the - # Windows API FindFirstFile. - # Other Windows APIs can fail or give incorrect results when - # dealing with files that are pending deletion. - L = os.listdir(dirname) - if not (L if waitall else name in L): - return - # Increase the timeout and try again - time.sleep(timeout) - timeout *= 2 - warnings.warn('tests may fail, delete still pending for ' + pathname, - RuntimeWarning, stacklevel=4) - - def _unlink(filename): - _waitfor(os.unlink, filename) - - def _rmdir(dirname): - _waitfor(os.rmdir, dirname) - - def _rmtree(path): - def _rmtree_inner(path): - for name in os.listdir(path): - fullname = os.path.join(path, name) - if os.path.isdir(fullname): - _waitfor(_rmtree_inner, fullname, waitall=True) - os.rmdir(fullname) - else: - os.unlink(fullname) - _waitfor(_rmtree_inner, path, waitall=True) - _waitfor(os.rmdir, path) -else: - _unlink = os.unlink - _rmdir = os.rmdir - _rmtree = shutil.rmtree - -def unlink(filename): - try: - _unlink(filename) - except OSError as error: - # The filename need not exist. - if error.errno not in (errno.ENOENT, errno.ENOTDIR): - raise - -def rmdir(dirname): - try: - _rmdir(dirname) - except OSError as error: - # The directory need not exist. - if error.errno != errno.ENOENT: - raise - -def rmtree(path): - try: - _rmtree(path) - except OSError as error: - if error.errno != errno.ENOENT: - raise - -def make_legacy_pyc(source): - """Move a PEP 3147 pyc/pyo file to its legacy pyc/pyo location. - - The choice of .pyc or .pyo extension is done based on the __debug__ flag - value. - - :param source: The file system path to the source file. The source file - does not need to exist, however the PEP 3147 pyc file must exist. - :return: The file system path to the legacy pyc file. - """ - pyc_file = imp.cache_from_source(source) - up_one = os.path.dirname(os.path.abspath(source)) - legacy_pyc = os.path.join(up_one, source + ('c' if __debug__ else 'o')) - os.rename(pyc_file, legacy_pyc) - return legacy_pyc - -def forget(modname): - """'Forget' a module was ever imported. - - This removes the module from sys.modules and deletes any PEP 3147 or - legacy .pyc and .pyo files. - """ - unload(modname) - for dirname in sys.path: - source = os.path.join(dirname, modname + '.py') - # It doesn't matter if they exist or not, unlink all possible - # combinations of PEP 3147 and legacy pyc and pyo files. - unlink(source + 'c') - unlink(source + 'o') - unlink(imp.cache_from_source(source, debug_override=True)) - unlink(imp.cache_from_source(source, debug_override=False)) - -# On some platforms, should not run gui test even if it is allowed -# in `use_resources'. -if sys.platform.startswith('win'): - import ctypes - import ctypes.wintypes - def _is_gui_available(): - UOI_FLAGS = 1 - WSF_VISIBLE = 0x0001 - class USEROBJECTFLAGS(ctypes.Structure): - _fields_ = [("fInherit", ctypes.wintypes.BOOL), - ("fReserved", ctypes.wintypes.BOOL), - ("dwFlags", ctypes.wintypes.DWORD)] - dll = ctypes.windll.user32 - h = dll.GetProcessWindowStation() - if not h: - raise ctypes.WinError() - uof = USEROBJECTFLAGS() - needed = ctypes.wintypes.DWORD() - res = dll.GetUserObjectInformationW(h, - UOI_FLAGS, - ctypes.byref(uof), - ctypes.sizeof(uof), - ctypes.byref(needed)) - if not res: - raise ctypes.WinError() - return bool(uof.dwFlags & WSF_VISIBLE) -else: - def _is_gui_available(): - return True - -def is_resource_enabled(resource): - """Test whether a resource is enabled. Known resources are set by - regrtest.py.""" - return use_resources is not None and resource in use_resources - -def requires(resource, msg=None): - """Raise ResourceDenied if the specified resource is not available. - - If the caller's module is __main__ then automatically return True. The - possibility of False being returned occurs when regrtest.py is - executing. - """ - if resource == 'gui' and not _is_gui_available(): - raise unittest.SkipTest("Cannot use the 'gui' resource") - # see if the caller's module is __main__ - if so, treat as if - # the resource was set - if sys._getframe(1).f_globals.get("__name__") == "__main__": - return - if not is_resource_enabled(resource): - if msg is None: - msg = "Use of the %r resource not enabled" % resource - raise ResourceDenied(msg) - -def _requires_unix_version(sysname, min_version): - """Decorator raising SkipTest if the OS is `sysname` and the version is less - than `min_version`. - - For example, @_requires_unix_version('FreeBSD', (7, 2)) raises SkipTest if - the FreeBSD version is less than 7.2. - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kw): - if platform.system() == sysname: - version_txt = platform.release().split('-', 1)[0] - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - pass - else: - if version < min_version: - min_version_txt = '.'.join(map(str, min_version)) - raise unittest.SkipTest( - "%s version %s or higher required, not %s" - % (sysname, min_version_txt, version_txt)) - return func(*args, **kw) - wrapper.min_version = min_version - return wrapper - return decorator - -def requires_freebsd_version(*min_version): - """Decorator raising SkipTest if the OS is FreeBSD and the FreeBSD version is - less than `min_version`. - - For example, @requires_freebsd_version(7, 2) raises SkipTest if the FreeBSD - version is less than 7.2. - """ - return _requires_unix_version('FreeBSD', min_version) - -def requires_linux_version(*min_version): - """Decorator raising SkipTest if the OS is Linux and the Linux version is - less than `min_version`. - - For example, @requires_linux_version(2, 6, 32) raises SkipTest if the Linux - version is less than 2.6.32. - """ - return _requires_unix_version('Linux', min_version) - -def requires_mac_ver(*min_version): - """Decorator raising SkipTest if the OS is Mac OS X and the OS X - version if less than min_version. - - For example, @requires_mac_ver(10, 5) raises SkipTest if the OS X version - is lesser than 10.5. - """ - def decorator(func): - @functools.wraps(func) - def wrapper(*args, **kw): - if sys.platform == 'darwin': - version_txt = platform.mac_ver()[0] - try: - version = tuple(map(int, version_txt.split('.'))) - except ValueError: - pass - else: - if version < min_version: - min_version_txt = '.'.join(map(str, min_version)) - raise unittest.SkipTest( - "Mac OS X %s or higher required, not %s" - % (min_version_txt, version_txt)) - return func(*args, **kw) - wrapper.min_version = min_version - return wrapper - return decorator - -# Don't use "localhost", since resolving it uses the DNS under recent -# Windows versions (see issue #18792). -HOST = "127.0.0.1" -HOSTv6 = "::1" - - -def find_unused_port(family=socket.AF_INET, socktype=socket.SOCK_STREAM): - """Returns an unused port that should be suitable for binding. This is - achieved by creating a temporary socket with the same family and type as - the 'sock' parameter (default is AF_INET, SOCK_STREAM), and binding it to - the specified host address (defaults to 0.0.0.0) with the port set to 0, - eliciting an unused ephemeral port from the OS. The temporary socket is - then closed and deleted, and the ephemeral port is returned. - - Either this method or bind_port() should be used for any tests where a - server socket needs to be bound to a particular port for the duration of - the test. Which one to use depends on whether the calling code is creating - a python socket, or if an unused port needs to be provided in a constructor - or passed to an external program (i.e. the -accept argument to openssl's - s_server mode). Always prefer bind_port() over find_unused_port() where - possible. Hard coded ports should *NEVER* be used. As soon as a server - socket is bound to a hard coded port, the ability to run multiple instances - of the test simultaneously on the same host is compromised, which makes the - test a ticking time bomb in a buildbot environment. On Unix buildbots, this - may simply manifest as a failed test, which can be recovered from without - intervention in most cases, but on Windows, the entire python process can - completely and utterly wedge, requiring someone to log in to the buildbot - and manually kill the affected process. - - (This is easy to reproduce on Windows, unfortunately, and can be traced to - the SO_REUSEADDR socket option having different semantics on Windows versus - Unix/Linux. On Unix, you can't have two AF_INET SOCK_STREAM sockets bind, - listen and then accept connections on identical host/ports. An EADDRINUSE - socket.error will be raised at some point (depending on the platform and - the order bind and listen were called on each socket). - - However, on Windows, if SO_REUSEADDR is set on the sockets, no EADDRINUSE - will ever be raised when attempting to bind two identical host/ports. When - accept() is called on each socket, the second caller's process will steal - the port from the first caller, leaving them both in an awkwardly wedged - state where they'll no longer respond to any signals or graceful kills, and - must be forcibly killed via OpenProcess()/TerminateProcess(). - - The solution on Windows is to use the SO_EXCLUSIVEADDRUSE socket option - instead of SO_REUSEADDR, which effectively affords the same semantics as - SO_REUSEADDR on Unix. Given the propensity of Unix developers in the Open - Source world compared to Windows ones, this is a common mistake. A quick - look over OpenSSL's 0.9.8g source shows that they use SO_REUSEADDR when - openssl.exe is called with the 's_server' option, for example. See - http://bugs.python.org/issue2550 for more info. The following site also - has a very thorough description about the implications of both REUSEADDR - and EXCLUSIVEADDRUSE on Windows: - http://msdn2.microsoft.com/en-us/library/ms740621(VS.85).aspx) - - XXX: although this approach is a vast improvement on previous attempts to - elicit unused ports, it rests heavily on the assumption that the ephemeral - port returned to us by the OS won't immediately be dished back out to some - other process when we close and delete our temporary socket but before our - calling code has a chance to bind the returned port. We can deal with this - issue if/when we come across it. - """ - - tempsock = socket.socket(family, socktype) - port = bind_port(tempsock) - tempsock.close() - del tempsock - return port - -def bind_port(sock, host=HOST): - """Bind the socket to a free port and return the port number. Relies on - ephemeral ports in order to ensure we are using an unbound port. This is - important as many tests may be running simultaneously, especially in a - buildbot environment. This method raises an exception if the sock.family - is AF_INET and sock.type is SOCK_STREAM, *and* the socket has SO_REUSEADDR - or SO_REUSEPORT set on it. Tests should *never* set these socket options - for TCP/IP sockets. The only case for setting these options is testing - multicasting via multiple UDP sockets. - - Additionally, if the SO_EXCLUSIVEADDRUSE socket option is available (i.e. - on Windows), it will be set on the socket. This will prevent anyone else - from bind()'ing to our host/port for the duration of the test. - """ - - if sock.family == socket.AF_INET and sock.type == socket.SOCK_STREAM: - if hasattr(socket, 'SO_REUSEADDR'): - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR) == 1: - raise TestFailed("tests should never set the SO_REUSEADDR " \ - "socket option on TCP/IP sockets!") - if hasattr(socket, 'SO_REUSEPORT'): - try: - if sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT) == 1: - raise TestFailed("tests should never set the SO_REUSEPORT " \ - "socket option on TCP/IP sockets!") - except socket.error: - # Python's socket module was compiled using modern headers - # thus defining SO_REUSEPORT but this process is running - # under an older kernel that does not support SO_REUSEPORT. - pass - if hasattr(socket, 'SO_EXCLUSIVEADDRUSE'): - sock.setsockopt(socket.SOL_SOCKET, socket.SO_EXCLUSIVEADDRUSE, 1) - - sock.bind((host, 0)) - port = sock.getsockname()[1] - return port - -def _is_ipv6_enabled(): - """Check whether IPv6 is enabled on this host.""" - if socket.has_ipv6: - sock = None - try: - sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) - sock.bind(('::1', 0)) - return True - except (socket.error, socket.gaierror): - pass - finally: - if sock: - sock.close() - return False - -IPV6_ENABLED = _is_ipv6_enabled() - - -# A constant likely larger than the underlying OS pipe buffer size, to -# make writes blocking. -# Windows limit seems to be around 512 B, and many Unix kernels have a -# 64 KiB pipe buffer size or 16 * PAGE_SIZE: take a few megs to be sure. -# (see issue #17835 for a discussion of this number). -PIPE_MAX_SIZE = 4 * 1024 * 1024 + 1 - -# A constant likely larger than the underlying OS socket buffer size, to make -# writes blocking. -# The socket buffer sizes can usually be tuned system-wide (e.g. through sysctl -# on Linux), or on a per-socket basis (SO_SNDBUF/SO_RCVBUF). See issue #18643 -# for a discussion of this number). -SOCK_MAX_SIZE = 16 * 1024 * 1024 + 1 - -# # decorator for skipping tests on non-IEEE 754 platforms -# requires_IEEE_754 = unittest.skipUnless( -# float.__getformat__("double").startswith("IEEE"), -# "test requires IEEE 754 doubles") - -requires_zlib = unittest.skipUnless(zlib, 'requires zlib') - -requires_bz2 = unittest.skipUnless(bz2, 'requires bz2') - -requires_lzma = unittest.skipUnless(lzma, 'requires lzma') - -is_jython = sys.platform.startswith('java') - -# Filename used for testing -if os.name == 'java': - # Jython disallows @ in module names - TESTFN = '$test' -else: - TESTFN = '@test' - -# Disambiguate TESTFN for parallel testing, while letting it remain a valid -# module name. -TESTFN = "{0}_{1}_tmp".format(TESTFN, os.getpid()) - -# # FS_NONASCII: non-ASCII character encodable by os.fsencode(), -# # or None if there is no such character. -# FS_NONASCII = None -# for character in ( -# # First try printable and common characters to have a readable filename. -# # For each character, the encoding list are just example of encodings able -# # to encode the character (the list is not exhaustive). -# -# # U+00E6 (Latin Small Letter Ae): cp1252, iso-8859-1 -# '\u00E6', -# # U+0130 (Latin Capital Letter I With Dot Above): cp1254, iso8859_3 -# '\u0130', -# # U+0141 (Latin Capital Letter L With Stroke): cp1250, cp1257 -# '\u0141', -# # U+03C6 (Greek Small Letter Phi): cp1253 -# '\u03C6', -# # U+041A (Cyrillic Capital Letter Ka): cp1251 -# '\u041A', -# # U+05D0 (Hebrew Letter Alef): Encodable to cp424 -# '\u05D0', -# # U+060C (Arabic Comma): cp864, cp1006, iso8859_6, mac_arabic -# '\u060C', -# # U+062A (Arabic Letter Teh): cp720 -# '\u062A', -# # U+0E01 (Thai Character Ko Kai): cp874 -# '\u0E01', -# -# # Then try more "special" characters. "special" because they may be -# # interpreted or displayed differently depending on the exact locale -# # encoding and the font. -# -# # U+00A0 (No-Break Space) -# '\u00A0', -# # U+20AC (Euro Sign) -# '\u20AC', -# ): -# try: -# os.fsdecode(os.fsencode(character)) -# except UnicodeError: -# pass -# else: -# FS_NONASCII = character -# break -# -# # TESTFN_UNICODE is a non-ascii filename -# TESTFN_UNICODE = TESTFN + "-\xe0\xf2\u0258\u0141\u011f" -# if sys.platform == 'darwin': -# # In Mac OS X's VFS API file names are, by definition, canonically -# # decomposed Unicode, encoded using UTF-8. See QA1173: -# # http://developer.apple.com/mac/library/qa/qa2001/qa1173.html -# import unicodedata -# TESTFN_UNICODE = unicodedata.normalize('NFD', TESTFN_UNICODE) -# TESTFN_ENCODING = sys.getfilesystemencoding() -# -# # TESTFN_UNENCODABLE is a filename (str type) that should *not* be able to be -# # encoded by the filesystem encoding (in strict mode). It can be None if we -# # cannot generate such filename. -# TESTFN_UNENCODABLE = None -# if os.name in ('nt', 'ce'): -# # skip win32s (0) or Windows 9x/ME (1) -# if sys.getwindowsversion().platform >= 2: -# # Different kinds of characters from various languages to minimize the -# # probability that the whole name is encodable to MBCS (issue #9819) -# TESTFN_UNENCODABLE = TESTFN + "-\u5171\u0141\u2661\u0363\uDC80" -# try: -# TESTFN_UNENCODABLE.encode(TESTFN_ENCODING) -# except UnicodeEncodeError: -# pass -# else: -# print('WARNING: The filename %r CAN be encoded by the filesystem encoding (%s). ' -# 'Unicode filename tests may not be effective' -# % (TESTFN_UNENCODABLE, TESTFN_ENCODING)) -# TESTFN_UNENCODABLE = None -# # Mac OS X denies unencodable filenames (invalid utf-8) -# elif sys.platform != 'darwin': -# try: -# # ascii and utf-8 cannot encode the byte 0xff -# b'\xff'.decode(TESTFN_ENCODING) -# except UnicodeDecodeError: -# # 0xff will be encoded using the surrogate character u+DCFF -# TESTFN_UNENCODABLE = TESTFN \ -# + b'-\xff'.decode(TESTFN_ENCODING, 'surrogateescape') -# else: -# # File system encoding (eg. ISO-8859-* encodings) can encode -# # the byte 0xff. Skip some unicode filename tests. -# pass -# -# # TESTFN_UNDECODABLE is a filename (bytes type) that should *not* be able to be -# # decoded from the filesystem encoding (in strict mode). It can be None if we -# # cannot generate such filename (ex: the latin1 encoding can decode any byte -# # sequence). On UNIX, TESTFN_UNDECODABLE can be decoded by os.fsdecode() thanks -# # to the surrogateescape error handler (PEP 383), but not from the filesystem -# # encoding in strict mode. -# TESTFN_UNDECODABLE = None -# for name in ( -# # b'\xff' is not decodable by os.fsdecode() with code page 932. Windows -# # accepts it to create a file or a directory, or don't accept to enter to -# # such directory (when the bytes name is used). So test b'\xe7' first: it is -# # not decodable from cp932. -# b'\xe7w\xf0', -# # undecodable from ASCII, UTF-8 -# b'\xff', -# # undecodable from iso8859-3, iso8859-6, iso8859-7, cp424, iso8859-8, cp856 -# # and cp857 -# b'\xae\xd5' -# # undecodable from UTF-8 (UNIX and Mac OS X) -# b'\xed\xb2\x80', b'\xed\xb4\x80', -# # undecodable from shift_jis, cp869, cp874, cp932, cp1250, cp1251, cp1252, -# # cp1253, cp1254, cp1255, cp1257, cp1258 -# b'\x81\x98', -# ): -# try: -# name.decode(TESTFN_ENCODING) -# except UnicodeDecodeError: -# TESTFN_UNDECODABLE = os.fsencode(TESTFN) + name -# break -# -# if FS_NONASCII: -# TESTFN_NONASCII = TESTFN + '-' + FS_NONASCII -# else: -# TESTFN_NONASCII = None - -# Save the initial cwd -SAVEDCWD = os.getcwd() - -@contextlib.contextmanager -def temp_cwd(name='tempcwd', quiet=False, path=None): - """ - Context manager that temporarily changes the CWD. - - An existing path may be provided as *path*, in which case this - function makes no changes to the file system. - - Otherwise, the new CWD is created in the current directory and it's - named *name*. If *quiet* is False (default) and it's not possible to - create or change the CWD, an error is raised. If it's True, only a - warning is raised and the original CWD is used. - """ - saved_dir = os.getcwd() - is_temporary = False - if path is None: - path = name - try: - os.mkdir(name) - is_temporary = True - except OSError: - if not quiet: - raise - warnings.warn('tests may fail, unable to create temp CWD ' + name, - RuntimeWarning, stacklevel=3) - try: - os.chdir(path) - except OSError: - if not quiet: - raise - warnings.warn('tests may fail, unable to change the CWD to ' + path, - RuntimeWarning, stacklevel=3) - try: - yield os.getcwd() - finally: - os.chdir(saved_dir) - if is_temporary: - rmtree(name) - - -if hasattr(os, "umask"): - @contextlib.contextmanager - def temp_umask(umask): - """Context manager that temporarily sets the process umask.""" - oldmask = os.umask(umask) - try: - yield - finally: - os.umask(oldmask) - - -def findfile(file, here=__file__, subdir=None): - """Try to find a file on sys.path and the working directory. If it is not - found the argument passed to the function is returned (this does not - necessarily signal failure; could still be the legitimate path).""" - if os.path.isabs(file): - return file - if subdir is not None: - file = os.path.join(subdir, file) - path = sys.path - path = [os.path.dirname(here)] + path - for dn in path: - fn = os.path.join(dn, file) - if os.path.exists(fn): return fn - return file - -def create_empty_file(filename): - """Create an empty file. If the file already exists, truncate it.""" - fd = os.open(filename, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) - os.close(fd) - -def sortdict(dict): - "Like repr(dict), but in sorted order." - items = sorted(dict.items()) - reprpairs = ["%r: %r" % pair for pair in items] - withcommas = ", ".join(reprpairs) - return "{%s}" % withcommas - -def make_bad_fd(): - """ - Create an invalid file descriptor by opening and closing a file and return - its fd. - """ - file = open(TESTFN, "wb") - try: - return file.fileno() - finally: - file.close() - unlink(TESTFN) - -def check_syntax_error(testcase, statement): - testcase.assertRaises(SyntaxError, compile, statement, - '', 'exec') - -def open_urlresource(url, *args, **kw): - from future.backports.urllib import (request as urllib_request, - parse as urllib_parse) - - check = kw.pop('check', None) - - filename = urllib_parse.urlparse(url)[2].split('/')[-1] # '/': it's URL! - - fn = os.path.join(os.path.dirname(__file__), "data", filename) - - def check_valid_file(fn): - f = open(fn, *args, **kw) - if check is None: - return f - elif check(f): - f.seek(0) - return f - f.close() - - if os.path.exists(fn): - f = check_valid_file(fn) - if f is not None: - return f - unlink(fn) - - # Verify the requirement before downloading the file - requires('urlfetch') - - print('\tfetching %s ...' % url, file=get_original_stdout()) - f = urllib_request.urlopen(url, timeout=15) - try: - with open(fn, "wb") as out: - s = f.read() - while s: - out.write(s) - s = f.read() - finally: - f.close() - - f = check_valid_file(fn) - if f is not None: - return f - raise TestFailed('invalid resource %r' % fn) - - -class WarningsRecorder(object): - """Convenience wrapper for the warnings list returned on - entry to the warnings.catch_warnings() context manager. - """ - def __init__(self, warnings_list): - self._warnings = warnings_list - self._last = 0 - - def __getattr__(self, attr): - if len(self._warnings) > self._last: - return getattr(self._warnings[-1], attr) - elif attr in warnings.WarningMessage._WARNING_DETAILS: - return None - raise AttributeError("%r has no attribute %r" % (self, attr)) - - @property - def warnings(self): - return self._warnings[self._last:] - - def reset(self): - self._last = len(self._warnings) - - -def _filterwarnings(filters, quiet=False): - """Catch the warnings, then check if all the expected - warnings have been raised and re-raise unexpected warnings. - If 'quiet' is True, only re-raise the unexpected warnings. - """ - # Clear the warning registry of the calling module - # in order to re-raise the warnings. - frame = sys._getframe(2) - registry = frame.f_globals.get('__warningregistry__') - if registry: - if utils.PY3: - registry.clear() - else: - # Py2-compatible: - for i in range(len(registry)): - registry.pop() - with warnings.catch_warnings(record=True) as w: - # Set filter "always" to record all warnings. Because - # test_warnings swap the module, we need to look up in - # the sys.modules dictionary. - sys.modules['warnings'].simplefilter("always") - yield WarningsRecorder(w) - # Filter the recorded warnings - reraise = list(w) - missing = [] - for msg, cat in filters: - seen = False - for w in reraise[:]: - warning = w.message - # Filter out the matching messages - if (re.match(msg, str(warning), re.I) and - issubclass(warning.__class__, cat)): - seen = True - reraise.remove(w) - if not seen and not quiet: - # This filter caught nothing - missing.append((msg, cat.__name__)) - if reraise: - raise AssertionError("unhandled warning %s" % reraise[0]) - if missing: - raise AssertionError("filter (%r, %s) did not catch any warning" % - missing[0]) - - -@contextlib.contextmanager -def check_warnings(*filters, **kwargs): - """Context manager to silence warnings. - - Accept 2-tuples as positional arguments: - ("message regexp", WarningCategory) - - Optional argument: - - if 'quiet' is True, it does not fail if a filter catches nothing - (default True without argument, - default False if some filters are defined) - - Without argument, it defaults to: - check_warnings(("", Warning), quiet=True) - """ - quiet = kwargs.get('quiet') - if not filters: - filters = (("", Warning),) - # Preserve backward compatibility - if quiet is None: - quiet = True - return _filterwarnings(filters, quiet) - - -class CleanImport(object): - """Context manager to force import to return a new module reference. - - This is useful for testing module-level behaviours, such as - the emission of a DeprecationWarning on import. - - Use like this: - - with CleanImport("foo"): - importlib.import_module("foo") # new reference - """ - - def __init__(self, *module_names): - self.original_modules = sys.modules.copy() - for module_name in module_names: - if module_name in sys.modules: - module = sys.modules[module_name] - # It is possible that module_name is just an alias for - # another module (e.g. stub for modules renamed in 3.x). - # In that case, we also need delete the real module to clear - # the import cache. - if module.__name__ != module_name: - del sys.modules[module.__name__] - del sys.modules[module_name] - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - sys.modules.update(self.original_modules) - -### Added for python-future: -if utils.PY3: - import collections.abc - mybase = collections.abc.MutableMapping -else: - import UserDict - mybase = UserDict.DictMixin -### - -class EnvironmentVarGuard(mybase): - - """Class to help protect the environment variable properly. Can be used as - a context manager.""" - - def __init__(self): - self._environ = os.environ - self._changed = {} - - def __getitem__(self, envvar): - return self._environ[envvar] - - def __setitem__(self, envvar, value): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - self._environ[envvar] = value - - def __delitem__(self, envvar): - # Remember the initial value on the first access - if envvar not in self._changed: - self._changed[envvar] = self._environ.get(envvar) - if envvar in self._environ: - del self._environ[envvar] - - def keys(self): - return self._environ.keys() - - def __iter__(self): - return iter(self._environ) - - def __len__(self): - return len(self._environ) - - def set(self, envvar, value): - self[envvar] = value - - def unset(self, envvar): - del self[envvar] - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - for (k, v) in self._changed.items(): - if v is None: - if k in self._environ: - del self._environ[k] - else: - self._environ[k] = v - os.environ = self._environ - - -class DirsOnSysPath(object): - """Context manager to temporarily add directories to sys.path. - - This makes a copy of sys.path, appends any directories given - as positional arguments, then reverts sys.path to the copied - settings when the context ends. - - Note that *all* sys.path modifications in the body of the - context manager, including replacement of the object, - will be reverted at the end of the block. - """ - - def __init__(self, *paths): - self.original_value = sys.path[:] - self.original_object = sys.path - sys.path.extend(paths) - - def __enter__(self): - return self - - def __exit__(self, *ignore_exc): - sys.path = self.original_object - sys.path[:] = self.original_value - - -class TransientResource(object): - - """Raise ResourceDenied if an exception is raised while the context manager - is in effect that matches the specified exception and attributes.""" - - def __init__(self, exc, **kwargs): - self.exc = exc - self.attrs = kwargs - - def __enter__(self): - return self - - def __exit__(self, type_=None, value=None, traceback=None): - """If type_ is a subclass of self.exc and value has attributes matching - self.attrs, raise ResourceDenied. Otherwise let the exception - propagate (if any).""" - if type_ is not None and issubclass(self.exc, type_): - for attr, attr_value in self.attrs.items(): - if not hasattr(value, attr): - break - if getattr(value, attr) != attr_value: - break - else: - raise ResourceDenied("an optional resource is not available") - -# Context managers that raise ResourceDenied when various issues -# with the Internet connection manifest themselves as exceptions. -# XXX deprecate these and use transient_internet() instead -time_out = TransientResource(IOError, errno=errno.ETIMEDOUT) -socket_peer_reset = TransientResource(socket.error, errno=errno.ECONNRESET) -ioerror_peer_reset = TransientResource(IOError, errno=errno.ECONNRESET) - - -@contextlib.contextmanager -def transient_internet(resource_name, timeout=30.0, errnos=()): - """Return a context manager that raises ResourceDenied when various issues - with the Internet connection manifest themselves as exceptions.""" - default_errnos = [ - ('ECONNREFUSED', 111), - ('ECONNRESET', 104), - ('EHOSTUNREACH', 113), - ('ENETUNREACH', 101), - ('ETIMEDOUT', 110), - ] - default_gai_errnos = [ - ('EAI_AGAIN', -3), - ('EAI_FAIL', -4), - ('EAI_NONAME', -2), - ('EAI_NODATA', -5), - # Encountered when trying to resolve IPv6-only hostnames - ('WSANO_DATA', 11004), - ] - - denied = ResourceDenied("Resource %r is not available" % resource_name) - captured_errnos = errnos - gai_errnos = [] - if not captured_errnos: - captured_errnos = [getattr(errno, name, num) - for (name, num) in default_errnos] - gai_errnos = [getattr(socket, name, num) - for (name, num) in default_gai_errnos] - - def filter_error(err): - n = getattr(err, 'errno', None) - if (isinstance(err, socket.timeout) or - (isinstance(err, socket.gaierror) and n in gai_errnos) or - n in captured_errnos): - if not verbose: - sys.stderr.write(denied.args[0] + "\n") - # Was: raise denied from err - # For Python-Future: - exc = denied - exc.__cause__ = err - raise exc - - old_timeout = socket.getdefaulttimeout() - try: - if timeout is not None: - socket.setdefaulttimeout(timeout) - yield - except IOError as err: - # urllib can wrap original socket errors multiple times (!), we must - # unwrap to get at the original error. - while True: - a = err.args - if len(a) >= 1 and isinstance(a[0], IOError): - err = a[0] - # The error can also be wrapped as args[1]: - # except socket.error as msg: - # raise IOError('socket error', msg).with_traceback(sys.exc_info()[2]) - elif len(a) >= 2 and isinstance(a[1], IOError): - err = a[1] - else: - break - filter_error(err) - raise - # XXX should we catch generic exceptions and look for their - # __cause__ or __context__? - finally: - socket.setdefaulttimeout(old_timeout) - - -@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.""" - import io - orig_stdout = getattr(sys, stream_name) - setattr(sys, stream_name, io.StringIO()) - 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 s: - print("hello") - self.assertEqual(s.getvalue(), "hello") - """ - return captured_output("stdout") - -def captured_stderr(): - return captured_output("stderr") - -def captured_stdin(): - return captured_output("stdin") - - -def gc_collect(): - """Force as many objects as possible to be collected. - - In non-CPython implementations of Python, this is needed because timely - deallocation is not guaranteed by the garbage collector. (Even in CPython - this can be the case in case of reference cycles.) This means that __del__ - methods may be called later than expected and weakrefs may remain alive for - longer than expected. This function tries its best to force all garbage - objects to disappear. - """ - gc.collect() - if is_jython: - time.sleep(0.1) - gc.collect() - gc.collect() - -@contextlib.contextmanager -def disable_gc(): - have_gc = gc.isenabled() - gc.disable() - try: - yield - finally: - if have_gc: - gc.enable() - - -def python_is_optimized(): - """Find if Python was built with optimizations.""" - # We don't have sysconfig on Py2.6: - import sysconfig - cflags = sysconfig.get_config_var('PY_CFLAGS') or '' - final_opt = "" - for opt in cflags.split(): - if opt.startswith('-O'): - final_opt = opt - return final_opt != '' and final_opt != '-O0' - - -_header = 'nP' -_align = '0n' -if hasattr(sys, "gettotalrefcount"): - _header = '2P' + _header - _align = '0P' -_vheader = _header + 'n' - -def calcobjsize(fmt): - return struct.calcsize(_header + fmt + _align) - -def calcvobjsize(fmt): - return struct.calcsize(_vheader + fmt + _align) - - -_TPFLAGS_HAVE_GC = 1<<14 -_TPFLAGS_HEAPTYPE = 1<<9 - -def check_sizeof(test, o, size): - result = sys.getsizeof(o) - # add GC header size - if ((type(o) == type) and (o.__flags__ & _TPFLAGS_HEAPTYPE) or\ - ((type(o) != type) and (type(o).__flags__ & _TPFLAGS_HAVE_GC))): - size += _testcapi.SIZEOF_PYGC_HEAD - msg = 'wrong size for %s: got %d, expected %d' \ - % (type(o), result, size) - test.assertEqual(result, size, msg) - -#======================================================================= -# Decorator for running a function in a different locale, correctly resetting -# it afterwards. - -def run_with_locale(catstr, *locales): - def decorator(func): - def inner(*args, **kwds): - try: - import locale - category = getattr(locale, catstr) - orig_locale = locale.setlocale(category) - except AttributeError: - # if the test author gives us an invalid category string - raise - except: - # cannot retrieve original locale, so do nothing - locale = orig_locale = None - else: - for loc in locales: - try: - locale.setlocale(category, loc) - break - except: - pass - - # now run the function, resetting the locale on exceptions - try: - return func(*args, **kwds) - finally: - if locale and orig_locale: - locale.setlocale(category, orig_locale) - inner.__name__ = func.__name__ - inner.__doc__ = func.__doc__ - return inner - return decorator - -#======================================================================= -# Decorator for running a function in a specific timezone, correctly -# resetting it afterwards. - -def run_with_tz(tz): - def decorator(func): - def inner(*args, **kwds): - try: - tzset = time.tzset - except AttributeError: - raise unittest.SkipTest("tzset required") - if 'TZ' in os.environ: - orig_tz = os.environ['TZ'] - else: - orig_tz = None - os.environ['TZ'] = tz - tzset() - - # now run the function, resetting the tz on exceptions - try: - return func(*args, **kwds) - finally: - if orig_tz is None: - del os.environ['TZ'] - else: - os.environ['TZ'] = orig_tz - time.tzset() - - inner.__name__ = func.__name__ - inner.__doc__ = func.__doc__ - return inner - return decorator - -#======================================================================= -# Big-memory-test support. Separate from 'resources' because memory use -# should be configurable. - -# Some handy shorthands. Note that these are used for byte-limits as well -# as size-limits, in the various bigmem tests -_1M = 1024*1024 -_1G = 1024 * _1M -_2G = 2 * _1G -_4G = 4 * _1G - -MAX_Py_ssize_t = sys.maxsize - -def set_memlimit(limit): - global max_memuse - global real_max_memuse - sizes = { - 'k': 1024, - 'm': _1M, - 'g': _1G, - 't': 1024*_1G, - } - m = re.match(r'(\d+(\.\d+)?) (K|M|G|T)b?$', limit, - re.IGNORECASE | re.VERBOSE) - if m is None: - raise ValueError('Invalid memory limit %r' % (limit,)) - memlimit = int(float(m.group(1)) * sizes[m.group(3).lower()]) - real_max_memuse = memlimit - if memlimit > MAX_Py_ssize_t: - memlimit = MAX_Py_ssize_t - if memlimit < _2G - 1: - raise ValueError('Memory limit %r too low to be useful' % (limit,)) - max_memuse = memlimit - -class _MemoryWatchdog(object): - """An object which periodically watches the process' memory consumption - and prints it out. - """ - - def __init__(self): - self.procfile = '/proc/{pid}/statm'.format(pid=os.getpid()) - self.started = False - - def start(self): - try: - f = open(self.procfile, 'r') - except OSError as e: - warnings.warn('/proc not available for stats: {0}'.format(e), - RuntimeWarning) - sys.stderr.flush() - return - - watchdog_script = findfile("memory_watchdog.py") - self.mem_watchdog = subprocess.Popen([sys.executable, watchdog_script], - stdin=f, stderr=subprocess.DEVNULL) - f.close() - self.started = True - - def stop(self): - if self.started: - self.mem_watchdog.terminate() - self.mem_watchdog.wait() - - -def bigmemtest(size, memuse, dry_run=True): - """Decorator for bigmem tests. - - 'minsize' is the minimum useful size for the test (in arbitrary, - test-interpreted units.) 'memuse' is the number of 'bytes per size' for - the test, or a good estimate of it. - - if 'dry_run' is False, it means the test doesn't support dummy runs - when -M is not specified. - """ - def decorator(f): - def wrapper(self): - size = wrapper.size - memuse = wrapper.memuse - if not real_max_memuse: - maxsize = 5147 - else: - maxsize = size - - if ((real_max_memuse or not dry_run) - and real_max_memuse < maxsize * memuse): - raise unittest.SkipTest( - "not enough memory: %.1fG minimum needed" - % (size * memuse / (1024 ** 3))) - - if real_max_memuse and verbose: - print() - print(" ... expected peak memory use: {peak:.1f}G" - .format(peak=size * memuse / (1024 ** 3))) - watchdog = _MemoryWatchdog() - watchdog.start() - else: - watchdog = None - - try: - return f(self, maxsize) - finally: - if watchdog: - watchdog.stop() - - wrapper.size = size - wrapper.memuse = memuse - return wrapper - return decorator - -def bigaddrspacetest(f): - """Decorator for tests that fill the address space.""" - def wrapper(self): - if max_memuse < MAX_Py_ssize_t: - if MAX_Py_ssize_t >= 2**63 - 1 and max_memuse >= 2**31: - raise unittest.SkipTest( - "not enough memory: try a 32-bit build instead") - else: - raise unittest.SkipTest( - "not enough memory: %.1fG minimum needed" - % (MAX_Py_ssize_t / (1024 ** 3))) - else: - return f(self) - return wrapper - -#======================================================================= -# unittest integration. - -class BasicTestRunner(object): - def run(self, test): - result = unittest.TestResult() - test(result) - return result - -def _id(obj): - return obj - -def requires_resource(resource): - if resource == 'gui' and not _is_gui_available(): - return unittest.skip("resource 'gui' is not available") - if is_resource_enabled(resource): - return _id - else: - return unittest.skip("resource {0!r} is not enabled".format(resource)) - -def cpython_only(test): - """ - Decorator for tests only applicable on CPython. - """ - return impl_detail(cpython=True)(test) - -def impl_detail(msg=None, **guards): - if check_impl_detail(**guards): - return _id - if msg is None: - guardnames, default = _parse_guards(guards) - if default: - msg = "implementation detail not available on {0}" - else: - msg = "implementation detail specific to {0}" - guardnames = sorted(guardnames.keys()) - msg = msg.format(' or '.join(guardnames)) - return unittest.skip(msg) - -def _parse_guards(guards): - # Returns a tuple ({platform_name: run_me}, default_value) - if not guards: - return ({'cpython': True}, False) - is_true = list(guards.values())[0] - assert list(guards.values()) == [is_true] * len(guards) # all True or all False - return (guards, not is_true) - -# Use the following check to guard CPython's implementation-specific tests -- -# or to run them only on the implementation(s) guarded by the arguments. -def check_impl_detail(**guards): - """This function returns True or False depending on the host platform. - Examples: - if check_impl_detail(): # only on CPython (default) - if check_impl_detail(jython=True): # only on Jython - if check_impl_detail(cpython=False): # everywhere except on CPython - """ - guards, default = _parse_guards(guards) - return guards.get(platform.python_implementation().lower(), default) - - -def no_tracing(func): - """Decorator to temporarily turn off tracing for the duration of a test.""" - if not hasattr(sys, 'gettrace'): - return func - else: - @functools.wraps(func) - def wrapper(*args, **kwargs): - original_trace = sys.gettrace() - try: - sys.settrace(None) - return func(*args, **kwargs) - finally: - sys.settrace(original_trace) - return wrapper - - -def refcount_test(test): - """Decorator for tests which involve reference counting. - - To start, the decorator does not run the test if is not run by CPython. - After that, any trace function is unset during the test to prevent - unexpected refcounts caused by the trace function. - - """ - return no_tracing(cpython_only(test)) - - -def _filter_suite(suite, pred): - """Recursively filter test cases in a suite based on a predicate.""" - newtests = [] - for test in suite._tests: - if isinstance(test, unittest.TestSuite): - _filter_suite(test, pred) - newtests.append(test) - else: - if pred(test): - newtests.append(test) - suite._tests = newtests - -def _run_suite(suite): - """Run tests from a unittest.TestSuite-derived class.""" - if verbose: - runner = unittest.TextTestRunner(sys.stdout, verbosity=2, - failfast=failfast) - else: - runner = BasicTestRunner() - - result = runner.run(suite) - if not result.wasSuccessful(): - if len(result.errors) == 1 and not result.failures: - err = result.errors[0][1] - elif len(result.failures) == 1 and not result.errors: - err = result.failures[0][1] - else: - err = "multiple errors occurred" - if not verbose: err += "; run in verbose mode for details" - raise TestFailed(err) - - -def run_unittest(*classes): - """Run tests from unittest.TestCase-derived classes.""" - valid_types = (unittest.TestSuite, unittest.TestCase) - suite = unittest.TestSuite() - for cls in classes: - if isinstance(cls, str): - if cls in sys.modules: - suite.addTest(unittest.findTestCases(sys.modules[cls])) - else: - raise ValueError("str arguments must be keys in sys.modules") - elif isinstance(cls, valid_types): - suite.addTest(cls) - else: - suite.addTest(unittest.makeSuite(cls)) - def case_pred(test): - if match_tests is None: - return True - for name in test.id().split("."): - if fnmatch.fnmatchcase(name, match_tests): - return True - return False - _filter_suite(suite, case_pred) - _run_suite(suite) - -# We don't have sysconfig on Py2.6: -# #======================================================================= -# # Check for the presence of docstrings. -# -# HAVE_DOCSTRINGS = (check_impl_detail(cpython=False) or -# sys.platform == 'win32' or -# sysconfig.get_config_var('WITH_DOC_STRINGS')) -# -# requires_docstrings = unittest.skipUnless(HAVE_DOCSTRINGS, -# "test requires docstrings") -# -# -# #======================================================================= -# doctest driver. - -def run_doctest(module, verbosity=None, optionflags=0): - """Run doctest on the given module. Return (#failures, #tests). - - If optional argument verbosity is not specified (or is None), pass - support's belief about verbosity on to doctest. Else doctest's - usual behavior is used (it searches sys.argv for -v). - """ - - import doctest - - if verbosity is None: - verbosity = verbose - else: - verbosity = None - - f, t = doctest.testmod(module, verbose=verbosity, optionflags=optionflags) - if f: - raise TestFailed("%d of %d doctests failed" % (f, t)) - if verbose: - print('doctest (%s) ... %d tests with zero failures' % - (module.__name__, t)) - return f, t - - -#======================================================================= -# Support for saving and restoring the imported modules. - -def modules_setup(): - return sys.modules.copy(), - -def modules_cleanup(oldmodules): - # Encoders/decoders are registered permanently within the internal - # codec cache. If we destroy the corresponding modules their - # globals will be set to None which will trip up the cached functions. - encodings = [(k, v) for k, v in sys.modules.items() - if k.startswith('encodings.')] - # Was: - # sys.modules.clear() - # Py2-compatible: - for i in range(len(sys.modules)): - sys.modules.pop() - - sys.modules.update(encodings) - # XXX: This kind of problem can affect more than just encodings. In particular - # extension modules (such as _ssl) don't cope with reloading properly. - # Really, test modules should be cleaning out the test specific modules they - # know they added (ala test_runpy) rather than relying on this function (as - # test_importhooks and test_pkg do currently). - # Implicitly imported *real* modules should be left alone (see issue 10556). - sys.modules.update(oldmodules) - -#======================================================================= -# Backported versions of threading_setup() and threading_cleanup() which don't refer -# to threading._dangling (not available on Py2.7). - -# Threading support to prevent reporting refleaks when running regrtest.py -R - -# NOTE: we use thread._count() rather than threading.enumerate() (or the -# moral equivalent thereof) because a threading.Thread object is still alive -# until its __bootstrap() method has returned, even after it has been -# unregistered from the threading module. -# thread._count(), on the other hand, only gets decremented *after* the -# __bootstrap() method has returned, which gives us reliable reference counts -# at the end of a test run. - -def threading_setup(): - if _thread: - return _thread._count(), - else: - return 1, - -def threading_cleanup(nb_threads): - if not _thread: - return - - _MAX_COUNT = 10 - for count in range(_MAX_COUNT): - n = _thread._count() - if n == nb_threads: - break - time.sleep(0.1) - # XXX print a warning in case of failure? - -def reap_threads(func): - """Use this function when threads are being used. This will - ensure that the threads are cleaned up even when the test fails. - If threading is unavailable this function does nothing. - """ - if not _thread: - return func - - @functools.wraps(func) - def decorator(*args): - key = threading_setup() - try: - return func(*args) - finally: - threading_cleanup(*key) - return decorator - -def reap_children(): - """Use this function at the end of test_main() whenever sub-processes - are started. This will help ensure that no extra children (zombies) - stick around to hog resources and create problems when looking - for refleaks. - """ - - # Reap all our dead child processes so we don't leave zombies around. - # These hog resources and might be causing some of the buildbots to die. - if hasattr(os, 'waitpid'): - any_process = -1 - while True: - try: - # This will raise an exception on Windows. That's ok. - pid, status = os.waitpid(any_process, os.WNOHANG) - if pid == 0: - break - except: - break - -@contextlib.contextmanager -def swap_attr(obj, attr, new_val): - """Temporary swap out an attribute with a new object. - - Usage: - with swap_attr(obj, "attr", 5): - ... - - This will set obj.attr to 5 for the duration of the with: block, - restoring the old value at the end of the block. If `attr` doesn't - exist on `obj`, it will be created and then deleted at the end of the - block. - """ - if hasattr(obj, attr): - real_val = getattr(obj, attr) - setattr(obj, attr, new_val) - try: - yield - finally: - setattr(obj, attr, real_val) - else: - setattr(obj, attr, new_val) - try: - yield - finally: - delattr(obj, attr) - -@contextlib.contextmanager -def swap_item(obj, item, new_val): - """Temporary swap out an item with a new object. - - Usage: - with swap_item(obj, "item", 5): - ... - - This will set obj["item"] to 5 for the duration of the with: block, - restoring the old value at the end of the block. If `item` doesn't - exist on `obj`, it will be created and then deleted at the end of the - block. - """ - if item in obj: - real_val = obj[item] - obj[item] = new_val - try: - yield - finally: - obj[item] = real_val - else: - obj[item] = new_val - try: - yield - finally: - del obj[item] - -def strip_python_stderr(stderr): - """Strip the stderr of a Python process from potential debug output - emitted by the interpreter. - - This will typically be run on the result of the communicate() method - of a subprocess.Popen object. - """ - stderr = re.sub(br"\[\d+ refs\]\r?\n?", b"", stderr).strip() - return stderr - -def args_from_interpreter_flags(): - """Return a list of command-line arguments reproducing the current - settings in sys.flags and sys.warnoptions.""" - return subprocess._args_from_interpreter_flags() - -#============================================================ -# Support for assertions about logging. -#============================================================ - -class TestHandler(logging.handlers.BufferingHandler): - def __init__(self, matcher): - # BufferingHandler takes a "capacity" argument - # so as to know when to flush. As we're overriding - # shouldFlush anyway, we can set a capacity of zero. - # You can call flush() manually to clear out the - # buffer. - logging.handlers.BufferingHandler.__init__(self, 0) - self.matcher = matcher - - def shouldFlush(self): - return False - - def emit(self, record): - self.format(record) - self.buffer.append(record.__dict__) - - def matches(self, **kwargs): - """ - Look for a saved dict whose keys/values match the supplied arguments. - """ - result = False - for d in self.buffer: - if self.matcher.matches(d, **kwargs): - result = True - break - return result - -class Matcher(object): - - _partial_matches = ('msg', 'message') - - def matches(self, d, **kwargs): - """ - Try to match a single dict with the supplied arguments. - - Keys whose values are strings and which are in self._partial_matches - will be checked for partial (i.e. substring) matches. You can extend - this scheme to (for example) do regular expression matching, etc. - """ - result = True - for k in kwargs: - v = kwargs[k] - dv = d.get(k) - if not self.match_value(k, dv, v): - result = False - break - return result - - def match_value(self, k, dv, v): - """ - Try to match a single stored value (dv) with a supplied value (v). - """ - if type(v) != type(dv): - result = False - elif type(dv) is not str or k not in self._partial_matches: - result = (v == dv) - else: - result = dv.find(v) >= 0 - return result - - -_can_symlink = None -def can_symlink(): - global _can_symlink - if _can_symlink is not None: - return _can_symlink - symlink_path = TESTFN + "can_symlink" - try: - os.symlink(TESTFN, symlink_path) - can = True - except (OSError, NotImplementedError, AttributeError): - can = False - else: - os.remove(symlink_path) - _can_symlink = can - return can - -def skip_unless_symlink(test): - """Skip decorator for tests that require functional symlink""" - ok = can_symlink() - msg = "Requires functional symlink implementation" - return test if ok else unittest.skip(msg)(test) - -_can_xattr = None -def can_xattr(): - global _can_xattr - if _can_xattr is not None: - return _can_xattr - if not hasattr(os, "setxattr"): - can = False - else: - tmp_fp, tmp_name = tempfile.mkstemp() - try: - with open(TESTFN, "wb") as fp: - try: - # TESTFN & tempfile may use different file systems with - # different capabilities - os.setxattr(tmp_fp, b"user.test", b"") - os.setxattr(fp.fileno(), b"user.test", b"") - # Kernels < 2.6.39 don't respect setxattr flags. - kernel_version = platform.release() - m = re.match("2.6.(\d{1,2})", kernel_version) - can = m is None or int(m.group(1)) >= 39 - except OSError: - can = False - finally: - unlink(TESTFN) - unlink(tmp_name) - _can_xattr = can - return can - -def skip_unless_xattr(test): - """Skip decorator for tests that require functional extended attributes""" - ok = can_xattr() - msg = "no non-broken extended attribute support" - return test if ok else unittest.skip(msg)(test) - - -if sys.platform.startswith('win'): - @contextlib.contextmanager - def suppress_crash_popup(): - """Disable Windows Error Reporting dialogs using SetErrorMode.""" - # see http://msdn.microsoft.com/en-us/library/windows/desktop/ms680621%28v=vs.85%29.aspx - # GetErrorMode is not available on Windows XP and Windows Server 2003, - # but SetErrorMode returns the previous value, so we can use that - import ctypes - k32 = ctypes.windll.kernel32 - SEM_NOGPFAULTERRORBOX = 0x02 - old_error_mode = k32.SetErrorMode(SEM_NOGPFAULTERRORBOX) - k32.SetErrorMode(old_error_mode | SEM_NOGPFAULTERRORBOX) - try: - yield - finally: - k32.SetErrorMode(old_error_mode) -else: - # this is a no-op for other platforms - @contextlib.contextmanager - def suppress_crash_popup(): - yield - - -def patch(test_instance, object_to_patch, attr_name, new_value): - """Override 'object_to_patch'.'attr_name' with 'new_value'. - - Also, add a cleanup procedure to 'test_instance' to restore - 'object_to_patch' value for 'attr_name'. - The 'attr_name' should be a valid attribute for 'object_to_patch'. - - """ - # check that 'attr_name' is a real attribute for 'object_to_patch' - # will raise AttributeError if it does not exist - getattr(object_to_patch, attr_name) - - # keep a copy of the old value - attr_is_local = False - try: - old_value = object_to_patch.__dict__[attr_name] - except (AttributeError, KeyError): - old_value = getattr(object_to_patch, attr_name, None) - else: - attr_is_local = True - - # restore the value when the test is done - def cleanup(): - if attr_is_local: - setattr(object_to_patch, attr_name, old_value) - else: - delattr(object_to_patch, attr_name) - - test_instance.addCleanup(cleanup) - - # actually override the attribute - setattr(object_to_patch, attr_name, new_value) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/total_ordering.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/total_ordering.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/total_ordering.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/total_ordering.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -""" -For Python < 2.7.2. total_ordering in versions prior to 2.7.2 is buggy. -See http://bugs.python.org/issue10042 for details. For these versions use -code borrowed from Python 2.7.3. - -From django.utils. -""" - -import sys -if sys.version_info >= (2, 7, 2): - from functools import total_ordering -else: - def total_ordering(cls): - """Class decorator that fills in missing ordering methods""" - convert = { - '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), - ('__le__', lambda self, other: self < other or self == other), - ('__ge__', lambda self, other: not self < other)], - '__le__': [('__ge__', lambda self, other: not self <= other or self == other), - ('__lt__', lambda self, other: self <= other and not self == other), - ('__gt__', lambda self, other: not self <= other)], - '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), - ('__ge__', lambda self, other: self > other or self == other), - ('__le__', lambda self, other: not self > other)], - '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), - ('__gt__', lambda self, other: self >= other and not self == other), - ('__lt__', lambda self, other: not self >= other)] - } - roots = set(dir(cls)) & set(convert) - if not roots: - raise ValueError('must define at least one ordering operation: < > <= >=') - root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ - for opname, opfunc in convert[root]: - if opname not in roots: - opfunc.__name__ = opname - opfunc.__doc__ = getattr(int, opname).__doc__ - setattr(cls, opname, opfunc) - return cls diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/error.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/error.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/error.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/error.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -"""Exception classes raised by urllib. - -The base exception class is URLError, which inherits from IOError. It -doesn't define any behavior of its own, but is the base class for all -exceptions defined in this package. - -HTTPError is an exception class that is also a valid HTTP response -instance. It behaves this way because HTTP protocol errors are valid -responses, with a status code, headers, and a body. In some contexts, -an application may want to handle an exception like a regular -response. -""" -from __future__ import absolute_import, division, unicode_literals -from future import standard_library - -from future.backports.urllib import response as urllib_response - - -__all__ = ['URLError', 'HTTPError', 'ContentTooShortError'] - - -# do these error classes make sense? -# make sure all of the IOError stuff is overridden. we just want to be -# subtypes. - -class URLError(IOError): - # URLError is a sub-type of IOError, but it doesn't share any of - # the implementation. need to override __init__ and __str__. - # It sets self.args for compatibility with other EnvironmentError - # subclasses, but args doesn't have the typical format with errno in - # slot 0 and strerror in slot 1. This may be better than nothing. - def __init__(self, reason, filename=None): - self.args = reason, - self.reason = reason - if filename is not None: - self.filename = filename - - def __str__(self): - return '' % self.reason - -class HTTPError(URLError, urllib_response.addinfourl): - """Raised when HTTP error occurs, but also acts like non-error return""" - __super_init = urllib_response.addinfourl.__init__ - - def __init__(self, url, code, msg, hdrs, fp): - self.code = code - self.msg = msg - self.hdrs = hdrs - self.fp = fp - self.filename = url - # The addinfourl classes depend on fp being a valid file - # object. In some cases, the HTTPError may not have a valid - # file object. If this happens, the simplest workaround is to - # not initialize the base classes. - if fp is not None: - self.__super_init(fp, hdrs, url, code) - - def __str__(self): - return 'HTTP Error %s: %s' % (self.code, self.msg) - - # since URLError specifies a .reason attribute, HTTPError should also - # provide this attribute. See issue13211 for discussion. - @property - def reason(self): - return self.msg - - def info(self): - return self.hdrs - - -# exception raised when downloaded size does not match content-length -class ContentTooShortError(URLError): - def __init__(self, message, content): - URLError.__init__(self, message) - self.content = content diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/parse.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/parse.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/parse.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/parse.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,991 +0,0 @@ -""" -Ported using Python-Future from the Python 3.3 standard library. - -Parse (absolute and relative) URLs. - -urlparse module is based upon the following RFC specifications. - -RFC 3986 (STD66): "Uniform Resource Identifiers" by T. Berners-Lee, R. Fielding -and L. Masinter, January 2005. - -RFC 2732 : "Format for Literal IPv6 Addresses in URL's by R.Hinden, B.Carpenter -and L.Masinter, December 1999. - -RFC 2396: "Uniform Resource Identifiers (URI)": Generic Syntax by T. -Berners-Lee, R. Fielding, and L. Masinter, August 1998. - -RFC 2368: "The mailto URL scheme", by P.Hoffman , L Masinter, J. Zawinski, July 1998. - -RFC 1808: "Relative Uniform Resource Locators", by R. Fielding, UC Irvine, June -1995. - -RFC 1738: "Uniform Resource Locators (URL)" by T. Berners-Lee, L. Masinter, M. -McCahill, December 1994 - -RFC 3986 is considered the current standard and any future changes to -urlparse module should conform with it. The urlparse module is -currently not entirely compliant with this RFC due to defacto -scenarios for parsing, and for backward compatibility purposes, some -parsing quirks from older RFCs are retained. The testcases in -test_urlparse.py provides a good indicator of parsing behavior. -""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import bytes, chr, dict, int, range, str -from future.utils import raise_with_traceback - -import re -import sys -import collections - -__all__ = ["urlparse", "urlunparse", "urljoin", "urldefrag", - "urlsplit", "urlunsplit", "urlencode", "parse_qs", - "parse_qsl", "quote", "quote_plus", "quote_from_bytes", - "unquote", "unquote_plus", "unquote_to_bytes"] - -# A classification of schemes ('' means apply by default) -uses_relative = ['ftp', 'http', 'gopher', 'nntp', 'imap', - 'wais', 'file', 'https', 'shttp', 'mms', - 'prospero', 'rtsp', 'rtspu', '', 'sftp', - 'svn', 'svn+ssh'] -uses_netloc = ['ftp', 'http', 'gopher', 'nntp', 'telnet', - 'imap', 'wais', 'file', 'mms', 'https', 'shttp', - 'snews', 'prospero', 'rtsp', 'rtspu', 'rsync', '', - 'svn', 'svn+ssh', 'sftp', 'nfs', 'git', 'git+ssh'] -uses_params = ['ftp', 'hdl', 'prospero', 'http', 'imap', - 'https', 'shttp', 'rtsp', 'rtspu', 'sip', 'sips', - 'mms', '', 'sftp', 'tel'] - -# These are not actually used anymore, but should stay for backwards -# compatibility. (They are undocumented, but have a public-looking name.) -non_hierarchical = ['gopher', 'hdl', 'mailto', 'news', - 'telnet', 'wais', 'imap', 'snews', 'sip', 'sips'] -uses_query = ['http', 'wais', 'imap', 'https', 'shttp', 'mms', - 'gopher', 'rtsp', 'rtspu', 'sip', 'sips', ''] -uses_fragment = ['ftp', 'hdl', 'http', 'gopher', 'news', - 'nntp', 'wais', 'https', 'shttp', 'snews', - 'file', 'prospero', ''] - -# Characters valid in scheme names -scheme_chars = ('abcdefghijklmnopqrstuvwxyz' - 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - '0123456789' - '+-.') - -# XXX: Consider replacing with functools.lru_cache -MAX_CACHE_SIZE = 20 -_parse_cache = {} - -def clear_cache(): - """Clear the parse cache and the quoters cache.""" - _parse_cache.clear() - _safe_quoters.clear() - - -# Helpers for bytes handling -# For 3.2, we deliberately require applications that -# handle improperly quoted URLs to do their own -# decoding and encoding. If valid use cases are -# presented, we may relax this by using latin-1 -# decoding internally for 3.3 -_implicit_encoding = 'ascii' -_implicit_errors = 'strict' - -def _noop(obj): - return obj - -def _encode_result(obj, encoding=_implicit_encoding, - errors=_implicit_errors): - return obj.encode(encoding, errors) - -def _decode_args(args, encoding=_implicit_encoding, - errors=_implicit_errors): - return tuple(x.decode(encoding, errors) if x else '' for x in args) - -def _coerce_args(*args): - # Invokes decode if necessary to create str args - # and returns the coerced inputs along with - # an appropriate result coercion function - # - noop for str inputs - # - encoding function otherwise - str_input = isinstance(args[0], str) - for arg in args[1:]: - # We special-case the empty string to support the - # "scheme=''" default argument to some functions - if arg and isinstance(arg, str) != str_input: - raise TypeError("Cannot mix str and non-str arguments") - if str_input: - return args + (_noop,) - return _decode_args(args) + (_encode_result,) - -# Result objects are more helpful than simple tuples -class _ResultMixinStr(object): - """Standard approach to encoding parsed results from str to bytes""" - __slots__ = () - - def encode(self, encoding='ascii', errors='strict'): - return self._encoded_counterpart(*(x.encode(encoding, errors) for x in self)) - - -class _ResultMixinBytes(object): - """Standard approach to decoding parsed results from bytes to str""" - __slots__ = () - - def decode(self, encoding='ascii', errors='strict'): - return self._decoded_counterpart(*(x.decode(encoding, errors) for x in self)) - - -class _NetlocResultMixinBase(object): - """Shared methods for the parsed result objects containing a netloc element""" - __slots__ = () - - @property - def username(self): - return self._userinfo[0] - - @property - def password(self): - return self._userinfo[1] - - @property - def hostname(self): - hostname = self._hostinfo[0] - if not hostname: - hostname = None - elif hostname is not None: - hostname = hostname.lower() - return hostname - - @property - def port(self): - port = self._hostinfo[1] - if port is not None: - port = int(port, 10) - # Return None on an illegal port - if not ( 0 <= port <= 65535): - return None - return port - - -class _NetlocResultMixinStr(_NetlocResultMixinBase, _ResultMixinStr): - __slots__ = () - - @property - def _userinfo(self): - netloc = self.netloc - userinfo, have_info, hostinfo = netloc.rpartition('@') - if have_info: - username, have_password, password = userinfo.partition(':') - if not have_password: - password = None - else: - username = password = None - return username, password - - @property - def _hostinfo(self): - netloc = self.netloc - _, _, hostinfo = netloc.rpartition('@') - _, have_open_br, bracketed = hostinfo.partition('[') - if have_open_br: - hostname, _, port = bracketed.partition(']') - _, have_port, port = port.partition(':') - else: - hostname, have_port, port = hostinfo.partition(':') - if not have_port: - port = None - return hostname, port - - -class _NetlocResultMixinBytes(_NetlocResultMixinBase, _ResultMixinBytes): - __slots__ = () - - @property - def _userinfo(self): - netloc = self.netloc - userinfo, have_info, hostinfo = netloc.rpartition(b'@') - if have_info: - username, have_password, password = userinfo.partition(b':') - if not have_password: - password = None - else: - username = password = None - return username, password - - @property - def _hostinfo(self): - netloc = self.netloc - _, _, hostinfo = netloc.rpartition(b'@') - _, have_open_br, bracketed = hostinfo.partition(b'[') - if have_open_br: - hostname, _, port = bracketed.partition(b']') - _, have_port, port = port.partition(b':') - else: - hostname, have_port, port = hostinfo.partition(b':') - if not have_port: - port = None - return hostname, port - - -from collections import namedtuple - -_DefragResultBase = namedtuple('DefragResult', 'url fragment') -_SplitResultBase = namedtuple('SplitResult', 'scheme netloc path query fragment') -_ParseResultBase = namedtuple('ParseResult', 'scheme netloc path params query fragment') - -# For backwards compatibility, alias _NetlocResultMixinStr -# ResultBase is no longer part of the documented API, but it is -# retained since deprecating it isn't worth the hassle -ResultBase = _NetlocResultMixinStr - -# Structured result objects for string data -class DefragResult(_DefragResultBase, _ResultMixinStr): - __slots__ = () - def geturl(self): - if self.fragment: - return self.url + '#' + self.fragment - else: - return self.url - -class SplitResult(_SplitResultBase, _NetlocResultMixinStr): - __slots__ = () - def geturl(self): - return urlunsplit(self) - -class ParseResult(_ParseResultBase, _NetlocResultMixinStr): - __slots__ = () - def geturl(self): - return urlunparse(self) - -# Structured result objects for bytes data -class DefragResultBytes(_DefragResultBase, _ResultMixinBytes): - __slots__ = () - def geturl(self): - if self.fragment: - return self.url + b'#' + self.fragment - else: - return self.url - -class SplitResultBytes(_SplitResultBase, _NetlocResultMixinBytes): - __slots__ = () - def geturl(self): - return urlunsplit(self) - -class ParseResultBytes(_ParseResultBase, _NetlocResultMixinBytes): - __slots__ = () - def geturl(self): - return urlunparse(self) - -# Set up the encode/decode result pairs -def _fix_result_transcoding(): - _result_pairs = ( - (DefragResult, DefragResultBytes), - (SplitResult, SplitResultBytes), - (ParseResult, ParseResultBytes), - ) - for _decoded, _encoded in _result_pairs: - _decoded._encoded_counterpart = _encoded - _encoded._decoded_counterpart = _decoded - -_fix_result_transcoding() -del _fix_result_transcoding - -def urlparse(url, scheme='', allow_fragments=True): - """Parse a URL into 6 components: - :///;?# - Return a 6-tuple: (scheme, netloc, path, params, query, fragment). - Note that we don't break the components up in smaller bits - (e.g. netloc is a single string) and we don't expand % escapes.""" - url, scheme, _coerce_result = _coerce_args(url, scheme) - splitresult = urlsplit(url, scheme, allow_fragments) - scheme, netloc, url, query, fragment = splitresult - if scheme in uses_params and ';' in url: - url, params = _splitparams(url) - else: - params = '' - result = ParseResult(scheme, netloc, url, params, query, fragment) - return _coerce_result(result) - -def _splitparams(url): - if '/' in url: - i = url.find(';', url.rfind('/')) - if i < 0: - return url, '' - else: - i = url.find(';') - return url[:i], url[i+1:] - -def _splitnetloc(url, start=0): - delim = len(url) # position of end of domain part of url, default is end - for c in '/?#': # look for delimiters; the order is NOT important - wdelim = url.find(c, start) # find first of this delim - if wdelim >= 0: # if found - delim = min(delim, wdelim) # use earliest delim position - return url[start:delim], url[delim:] # return (domain, rest) - -def urlsplit(url, scheme='', allow_fragments=True): - """Parse a URL into 5 components: - :///?# - Return a 5-tuple: (scheme, netloc, path, query, fragment). - Note that we don't break the components up in smaller bits - (e.g. netloc is a single string) and we don't expand % escapes.""" - url, scheme, _coerce_result = _coerce_args(url, scheme) - allow_fragments = bool(allow_fragments) - key = url, scheme, allow_fragments, type(url), type(scheme) - cached = _parse_cache.get(key, None) - if cached: - return _coerce_result(cached) - if len(_parse_cache) >= MAX_CACHE_SIZE: # avoid runaway growth - clear_cache() - netloc = query = fragment = '' - i = url.find(':') - if i > 0: - if url[:i] == 'http': # optimize the common case - scheme = url[:i].lower() - url = url[i+1:] - if url[:2] == '//': - netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return _coerce_result(v) - for c in url[:i]: - if c not in scheme_chars: - break - else: - # make sure "url" is not actually a port number (in which case - # "scheme" is really part of the path) - rest = url[i+1:] - if not rest or any(c not in '0123456789' for c in rest): - # not a port number - scheme, url = url[:i].lower(), rest - - if url[:2] == '//': - netloc, url = _splitnetloc(url, 2) - if (('[' in netloc and ']' not in netloc) or - (']' in netloc and '[' not in netloc)): - raise ValueError("Invalid IPv6 URL") - if allow_fragments and '#' in url: - url, fragment = url.split('#', 1) - if '?' in url: - url, query = url.split('?', 1) - v = SplitResult(scheme, netloc, url, query, fragment) - _parse_cache[key] = v - return _coerce_result(v) - -def urlunparse(components): - """Put a parsed URL back together again. This may result in a - slightly different, but equivalent URL, if the URL that was parsed - originally had redundant delimiters, e.g. a ? with an empty query - (the draft states that these are equivalent).""" - scheme, netloc, url, params, query, fragment, _coerce_result = ( - _coerce_args(*components)) - if params: - url = "%s;%s" % (url, params) - return _coerce_result(urlunsplit((scheme, netloc, url, query, fragment))) - -def urlunsplit(components): - """Combine the elements of a tuple as returned by urlsplit() into a - complete URL as a string. The data argument can be any five-item iterable. - This may result in a slightly different, but equivalent URL, if the URL that - was parsed originally had unnecessary delimiters (for example, a ? with an - empty query; the RFC states that these are equivalent).""" - scheme, netloc, url, query, fragment, _coerce_result = ( - _coerce_args(*components)) - if netloc or (scheme and scheme in uses_netloc and url[:2] != '//'): - if url and url[:1] != '/': url = '/' + url - url = '//' + (netloc or '') + url - if scheme: - url = scheme + ':' + url - if query: - url = url + '?' + query - if fragment: - url = url + '#' + fragment - return _coerce_result(url) - -def urljoin(base, url, allow_fragments=True): - """Join a base URL and a possibly relative URL to form an absolute - interpretation of the latter.""" - if not base: - return url - if not url: - return base - base, url, _coerce_result = _coerce_args(base, url) - bscheme, bnetloc, bpath, bparams, bquery, bfragment = \ - urlparse(base, '', allow_fragments) - scheme, netloc, path, params, query, fragment = \ - urlparse(url, bscheme, allow_fragments) - if scheme != bscheme or scheme not in uses_relative: - return _coerce_result(url) - if scheme in uses_netloc: - if netloc: - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - netloc = bnetloc - if path[:1] == '/': - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - if not path and not params: - path = bpath - params = bparams - if not query: - query = bquery - return _coerce_result(urlunparse((scheme, netloc, path, - params, query, fragment))) - segments = bpath.split('/')[:-1] + path.split('/') - # XXX The stuff below is bogus in various ways... - if segments[-1] == '.': - segments[-1] = '' - while '.' in segments: - segments.remove('.') - while 1: - i = 1 - n = len(segments) - 1 - while i < n: - if (segments[i] == '..' - and segments[i-1] not in ('', '..')): - del segments[i-1:i+1] - break - i = i+1 - else: - break - if segments == ['', '..']: - segments[-1] = '' - elif len(segments) >= 2 and segments[-1] == '..': - segments[-2:] = [''] - return _coerce_result(urlunparse((scheme, netloc, '/'.join(segments), - params, query, fragment))) - -def urldefrag(url): - """Removes any existing fragment from URL. - - Returns a tuple of the defragmented URL and the fragment. If - the URL contained no fragments, the second element is the - empty string. - """ - url, _coerce_result = _coerce_args(url) - if '#' in url: - s, n, p, a, q, frag = urlparse(url) - defrag = urlunparse((s, n, p, a, q, '')) - else: - frag = '' - defrag = url - return _coerce_result(DefragResult(defrag, frag)) - -_hexdig = '0123456789ABCDEFabcdef' -_hextobyte = dict(((a + b).encode(), bytes([int(a + b, 16)])) - for a in _hexdig for b in _hexdig) - -def unquote_to_bytes(string): - """unquote_to_bytes('abc%20def') -> b'abc def'.""" - # Note: strings are encoded as UTF-8. This is only an issue if it contains - # unescaped non-ASCII characters, which URIs should not. - if not string: - # Is it a string-like object? - string.split - return bytes(b'') - if isinstance(string, str): - string = string.encode('utf-8') - ### For Python-Future: - # It is already a byte-string object, but force it to be newbytes here on - # Py2: - string = bytes(string) - ### - bits = string.split(b'%') - if len(bits) == 1: - return string - res = [bits[0]] - append = res.append - for item in bits[1:]: - try: - append(_hextobyte[item[:2]]) - append(item[2:]) - except KeyError: - append(b'%') - append(item) - return bytes(b'').join(res) - -_asciire = re.compile('([\x00-\x7f]+)') - -def unquote(string, encoding='utf-8', errors='replace'): - """Replace %xx escapes by their single-character equivalent. The optional - encoding and errors parameters specify how to decode percent-encoded - sequences into Unicode characters, as accepted by the bytes.decode() - method. - By default, percent-encoded sequences are decoded with UTF-8, and invalid - sequences are replaced by a placeholder character. - - unquote('abc%20def') -> 'abc def'. - """ - if '%' not in string: - string.split - return string - if encoding is None: - encoding = 'utf-8' - if errors is None: - errors = 'replace' - bits = _asciire.split(string) - res = [bits[0]] - append = res.append - for i in range(1, len(bits), 2): - append(unquote_to_bytes(bits[i]).decode(encoding, errors)) - append(bits[i + 1]) - return ''.join(res) - -def parse_qs(qs, keep_blank_values=False, strict_parsing=False, - encoding='utf-8', errors='replace'): - """Parse a query given as a string argument. - - Arguments: - - qs: percent-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - percent-encoded queries should be treated as blank strings. - A true value indicates that blanks should be retained as - blank strings. The default false value indicates that - blank values are to be ignored and treated as if they were - not included. - - strict_parsing: flag indicating what to do with parsing errors. - If false (the default), errors are silently ignored. - If true, errors raise a ValueError exception. - - encoding and errors: specify how to decode percent-encoded sequences - into Unicode characters, as accepted by the bytes.decode() method. - """ - parsed_result = {} - pairs = parse_qsl(qs, keep_blank_values, strict_parsing, - encoding=encoding, errors=errors) - for name, value in pairs: - if name in parsed_result: - parsed_result[name].append(value) - else: - parsed_result[name] = [value] - return parsed_result - -def parse_qsl(qs, keep_blank_values=False, strict_parsing=False, - encoding='utf-8', errors='replace'): - """Parse a query given as a string argument. - - Arguments: - - qs: percent-encoded query string to be parsed - - keep_blank_values: flag indicating whether blank values in - percent-encoded queries should be treated as blank strings. A - true value indicates that blanks should be retained as blank - strings. The default false value indicates that blank values - are to be ignored and treated as if they were not included. - - strict_parsing: flag indicating what to do with parsing errors. If - false (the default), errors are silently ignored. If true, - errors raise a ValueError exception. - - encoding and errors: specify how to decode percent-encoded sequences - into Unicode characters, as accepted by the bytes.decode() method. - - Returns a list, as G-d intended. - """ - qs, _coerce_result = _coerce_args(qs) - pairs = [s2 for s1 in qs.split('&') for s2 in s1.split(';')] - r = [] - for name_value in pairs: - if not name_value and not strict_parsing: - continue - nv = name_value.split('=', 1) - if len(nv) != 2: - if strict_parsing: - raise ValueError("bad query field: %r" % (name_value,)) - # Handle case of a control-name with no equal sign - if keep_blank_values: - nv.append('') - else: - continue - if len(nv[1]) or keep_blank_values: - name = nv[0].replace('+', ' ') - name = unquote(name, encoding=encoding, errors=errors) - name = _coerce_result(name) - value = nv[1].replace('+', ' ') - value = unquote(value, encoding=encoding, errors=errors) - value = _coerce_result(value) - r.append((name, value)) - return r - -def unquote_plus(string, encoding='utf-8', errors='replace'): - """Like unquote(), but also replace plus signs by spaces, as required for - unquoting HTML form values. - - unquote_plus('%7e/abc+def') -> '~/abc def' - """ - string = string.replace('+', ' ') - return unquote(string, encoding, errors) - -_ALWAYS_SAFE = frozenset(bytes(b'ABCDEFGHIJKLMNOPQRSTUVWXYZ' - b'abcdefghijklmnopqrstuvwxyz' - b'0123456789' - b'_.-')) -_ALWAYS_SAFE_BYTES = bytes(_ALWAYS_SAFE) -_safe_quoters = {} - -class Quoter(collections.defaultdict): - """A mapping from bytes (in range(0,256)) to strings. - - String values are percent-encoded byte values, unless the key < 128, and - in the "safe" set (either the specified safe set, or default set). - """ - # Keeps a cache internally, using defaultdict, for efficiency (lookups - # of cached keys don't call Python code at all). - def __init__(self, safe): - """safe: bytes object.""" - self.safe = _ALWAYS_SAFE.union(bytes(safe)) - - def __repr__(self): - # Without this, will just display as a defaultdict - return "" % dict(self) - - def __missing__(self, b): - # Handle a cache miss. Store quoted string in cache and return. - res = chr(b) if b in self.safe else '%{0:02X}'.format(b) - self[b] = res - return res - -def quote(string, safe='/', encoding=None, errors=None): - """quote('abc def') -> 'abc%20def' - - Each part of a URL, e.g. the path info, the query, etc., has a - different set of reserved characters that must be quoted. - - RFC 2396 Uniform Resource Identifiers (URI): Generic Syntax lists - the following reserved characters. - - reserved = ";" | "/" | "?" | ":" | "@" | "&" | "=" | "+" | - "$" | "," - - Each of these characters is reserved in some component of a URL, - but not necessarily in all of them. - - By default, the quote function is intended for quoting the path - section of a URL. Thus, it will not encode '/'. This character - is reserved, but in typical usage the quote function is being - called on a path where the existing slash characters are used as - reserved characters. - - string and safe may be either str or bytes objects. encoding must - not be specified if string is a str. - - The optional encoding and errors parameters specify how to deal with - non-ASCII characters, as accepted by the str.encode method. - By default, encoding='utf-8' (characters are encoded with UTF-8), and - errors='strict' (unsupported characters raise a UnicodeEncodeError). - """ - if isinstance(string, str): - if not string: - return string - if encoding is None: - encoding = 'utf-8' - if errors is None: - errors = 'strict' - string = string.encode(encoding, errors) - else: - if encoding is not None: - raise TypeError("quote() doesn't support 'encoding' for bytes") - if errors is not None: - raise TypeError("quote() doesn't support 'errors' for bytes") - return quote_from_bytes(string, safe) - -def quote_plus(string, safe='', encoding=None, errors=None): - """Like quote(), but also replace ' ' with '+', as required for quoting - HTML form values. Plus signs in the original string are escaped unless - they are included in safe. It also does not have safe default to '/'. - """ - # Check if ' ' in string, where string may either be a str or bytes. If - # there are no spaces, the regular quote will produce the right answer. - if ((isinstance(string, str) and ' ' not in string) or - (isinstance(string, bytes) and b' ' not in string)): - return quote(string, safe, encoding, errors) - if isinstance(safe, str): - space = str(' ') - else: - space = bytes(b' ') - string = quote(string, safe + space, encoding, errors) - return string.replace(' ', '+') - -def quote_from_bytes(bs, safe='/'): - """Like quote(), but accepts a bytes object rather than a str, and does - not perform string-to-bytes encoding. It always returns an ASCII string. - quote_from_bytes(b'abc def\x3f') -> 'abc%20def%3f' - """ - if not isinstance(bs, (bytes, bytearray)): - raise TypeError("quote_from_bytes() expected bytes") - if not bs: - return str('') - ### For Python-Future: - bs = bytes(bs) - ### - if isinstance(safe, str): - # Normalize 'safe' by converting to bytes and removing non-ASCII chars - safe = str(safe).encode('ascii', 'ignore') - else: - ### For Python-Future: - safe = bytes(safe) - ### - safe = bytes([c for c in safe if c < 128]) - if not bs.rstrip(_ALWAYS_SAFE_BYTES + safe): - return bs.decode() - try: - quoter = _safe_quoters[safe] - except KeyError: - _safe_quoters[safe] = quoter = Quoter(safe).__getitem__ - return str('').join([quoter(char) for char in bs]) - -def urlencode(query, doseq=False, safe='', encoding=None, errors=None): - """Encode a sequence of two-element tuples or dictionary into a URL query string. - - If any values in the query arg are sequences and doseq is true, each - sequence element is converted to a separate parameter. - - If the query arg is a sequence of two-element tuples, the order of the - parameters in the output will match the order of parameters in the - input. - - The query arg may be either a string or a bytes type. When query arg is a - string, the safe, encoding and error parameters are sent the quote_plus for - encoding. - """ - - if hasattr(query, "items"): - query = query.items() - else: - # It's a bother at times that strings and string-like objects are - # sequences. - try: - # non-sequence items should not work with len() - # non-empty strings will fail this - if len(query) and not isinstance(query[0], tuple): - raise TypeError - # Zero-length sequences of all types will get here and succeed, - # but that's a minor nit. Since the original implementation - # allowed empty dicts that type of behavior probably should be - # preserved for consistency - except TypeError: - ty, va, tb = sys.exc_info() - raise_with_traceback(TypeError("not a valid non-string sequence " - "or mapping object"), tb) - - l = [] - if not doseq: - for k, v in query: - if isinstance(k, bytes): - k = quote_plus(k, safe) - else: - k = quote_plus(str(k), safe, encoding, errors) - - if isinstance(v, bytes): - v = quote_plus(v, safe) - else: - v = quote_plus(str(v), safe, encoding, errors) - l.append(k + '=' + v) - else: - for k, v in query: - if isinstance(k, bytes): - k = quote_plus(k, safe) - else: - k = quote_plus(str(k), safe, encoding, errors) - - if isinstance(v, bytes): - v = quote_plus(v, safe) - l.append(k + '=' + v) - elif isinstance(v, str): - v = quote_plus(v, safe, encoding, errors) - l.append(k + '=' + v) - else: - try: - # Is this a sufficient test for sequence-ness? - x = len(v) - except TypeError: - # not a sequence - v = quote_plus(str(v), safe, encoding, errors) - l.append(k + '=' + v) - else: - # loop over the sequence - for elt in v: - if isinstance(elt, bytes): - elt = quote_plus(elt, safe) - else: - elt = quote_plus(str(elt), safe, encoding, errors) - l.append(k + '=' + elt) - return str('&').join(l) - -# Utilities to parse URLs (most of these return None for missing parts): -# unwrap('') --> 'type://host/path' -# splittype('type:opaquestring') --> 'type', 'opaquestring' -# splithost('//host[:port]/path') --> 'host[:port]', '/path' -# splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]' -# splitpasswd('user:passwd') -> 'user', 'passwd' -# splitport('host:port') --> 'host', 'port' -# splitquery('/path?query') --> '/path', 'query' -# splittag('/path#tag') --> '/path', 'tag' -# splitattr('/path;attr1=value1;attr2=value2;...') -> -# '/path', ['attr1=value1', 'attr2=value2', ...] -# splitvalue('attr=value') --> 'attr', 'value' -# urllib.parse.unquote('abc%20def') -> 'abc def' -# quote('abc def') -> 'abc%20def') - -def to_bytes(url): - """to_bytes(u"URL") --> 'URL'.""" - # Most URL schemes require ASCII. If that changes, the conversion - # can be relaxed. - # XXX get rid of to_bytes() - if isinstance(url, str): - try: - url = url.encode("ASCII").decode() - except UnicodeError: - raise UnicodeError("URL " + repr(url) + - " contains non-ASCII characters") - return url - -def unwrap(url): - """unwrap('') --> 'type://host/path'.""" - url = str(url).strip() - if url[:1] == '<' and url[-1:] == '>': - url = url[1:-1].strip() - if url[:4] == 'URL:': url = url[4:].strip() - return url - -_typeprog = None -def splittype(url): - """splittype('type:opaquestring') --> 'type', 'opaquestring'.""" - global _typeprog - if _typeprog is None: - import re - _typeprog = re.compile('^([^/:]+):') - - match = _typeprog.match(url) - if match: - scheme = match.group(1) - return scheme.lower(), url[len(scheme) + 1:] - return None, url - -_hostprog = None -def splithost(url): - """splithost('//host[:port]/path') --> 'host[:port]', '/path'.""" - global _hostprog - if _hostprog is None: - import re - _hostprog = re.compile('^//([^/?]*)(.*)$') - - match = _hostprog.match(url) - if match: - host_port = match.group(1) - path = match.group(2) - if path and not path.startswith('/'): - path = '/' + path - return host_port, path - return None, url - -_userprog = None -def splituser(host): - """splituser('user[:passwd]@host[:port]') --> 'user[:passwd]', 'host[:port]'.""" - global _userprog - if _userprog is None: - import re - _userprog = re.compile('^(.*)@(.*)$') - - match = _userprog.match(host) - if match: return match.group(1, 2) - return None, host - -_passwdprog = None -def splitpasswd(user): - """splitpasswd('user:passwd') -> 'user', 'passwd'.""" - global _passwdprog - if _passwdprog is None: - import re - _passwdprog = re.compile('^([^:]*):(.*)$',re.S) - - match = _passwdprog.match(user) - if match: return match.group(1, 2) - return user, None - -# splittag('/path#tag') --> '/path', 'tag' -_portprog = None -def splitport(host): - """splitport('host:port') --> 'host', 'port'.""" - global _portprog - if _portprog is None: - import re - _portprog = re.compile('^(.*):([0-9]+)$') - - match = _portprog.match(host) - if match: return match.group(1, 2) - return host, None - -_nportprog = None -def splitnport(host, defport=-1): - """Split host and port, returning numeric port. - Return given default port if no ':' found; defaults to -1. - Return numerical port if a valid number are found after ':'. - Return None if ':' but not a valid number.""" - global _nportprog - if _nportprog is None: - import re - _nportprog = re.compile('^(.*):(.*)$') - - match = _nportprog.match(host) - if match: - host, port = match.group(1, 2) - try: - if not port: raise ValueError("no digits") - nport = int(port) - except ValueError: - nport = None - return host, nport - return host, defport - -_queryprog = None -def splitquery(url): - """splitquery('/path?query') --> '/path', 'query'.""" - global _queryprog - if _queryprog is None: - import re - _queryprog = re.compile('^(.*)\?([^?]*)$') - - match = _queryprog.match(url) - if match: return match.group(1, 2) - return url, None - -_tagprog = None -def splittag(url): - """splittag('/path#tag') --> '/path', 'tag'.""" - global _tagprog - if _tagprog is None: - import re - _tagprog = re.compile('^(.*)#([^#]*)$') - - match = _tagprog.match(url) - if match: return match.group(1, 2) - return url, None - -def splitattr(url): - """splitattr('/path;attr1=value1;attr2=value2;...') -> - '/path', ['attr1=value1', 'attr2=value2', ...].""" - words = url.split(';') - return words[0], words[1:] - -_valueprog = None -def splitvalue(attr): - """splitvalue('attr=value') --> 'attr', 'value'.""" - global _valueprog - if _valueprog is None: - import re - _valueprog = re.compile('^([^=]*)=(.*)$') - - match = _valueprog.match(attr) - if match: return match.group(1, 2) - return attr, None diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/request.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/request.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/request.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/request.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2641 +0,0 @@ -""" -Ported using Python-Future from the Python 3.3 standard library. - -An extensible library for opening URLs using a variety of protocols - -The simplest way to use this module is to call the urlopen function, -which accepts a string containing a URL or a Request object (described -below). It opens the URL and returns the results as file-like -object; the returned object has some extra methods described below. - -The OpenerDirector manages a collection of Handler objects that do -all the actual work. Each Handler implements a particular protocol or -option. The OpenerDirector is a composite object that invokes the -Handlers needed to open the requested URL. For example, the -HTTPHandler performs HTTP GET and POST requests and deals with -non-error returns. The HTTPRedirectHandler automatically deals with -HTTP 301, 302, 303 and 307 redirect errors, and the HTTPDigestAuthHandler -deals with digest authentication. - -urlopen(url, data=None) -- Basic usage is the same as original -urllib. pass the url and optionally data to post to an HTTP URL, and -get a file-like object back. One difference is that you can also pass -a Request instance instead of URL. Raises a URLError (subclass of -IOError); for HTTP errors, raises an HTTPError, which can also be -treated as a valid response. - -build_opener -- Function that creates a new OpenerDirector instance. -Will install the default handlers. Accepts one or more Handlers as -arguments, either instances or Handler classes that it will -instantiate. If one of the argument is a subclass of the default -handler, the argument will be installed instead of the default. - -install_opener -- Installs a new opener as the default opener. - -objects of interest: - -OpenerDirector -- Sets up the User Agent as the Python-urllib client and manages -the Handler classes, while dealing with requests and responses. - -Request -- An object that encapsulates the state of a request. The -state can be as simple as the URL. It can also include extra HTTP -headers, e.g. a User-Agent. - -BaseHandler -- - -internals: -BaseHandler and parent -_call_chain conventions - -Example usage: - -import urllib.request - -# set up authentication info -authinfo = urllib.request.HTTPBasicAuthHandler() -authinfo.add_password(realm='PDQ Application', - uri='https://mahler:8092/site-updates.py', - user='klem', - passwd='geheim$parole') - -proxy_support = urllib.request.ProxyHandler({"http" : "http://ahad-haam:3128"}) - -# build a new opener that adds authentication and caching FTP handlers -opener = urllib.request.build_opener(proxy_support, authinfo, - urllib.request.CacheFTPHandler) - -# install it -urllib.request.install_opener(opener) - -f = urllib.request.urlopen('http://www.python.org/') -""" - -# XXX issues: -# If an authentication error handler that tries to perform -# authentication for some reason but fails, how should the error be -# signalled? The client needs to know the HTTP error code. But if -# the handler knows that the problem was, e.g., that it didn't know -# that hash algo that requested in the challenge, it would be good to -# pass that information along to the client, too. -# ftp errors aren't handled cleanly -# check digest against correct (i.e. non-apache) implementation - -# Possible extensions: -# complex proxies XXX not sure what exactly was meant by this -# abstract factory for opener - -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import bytes, dict, filter, input, int, map, open, str -from future.utils import PY2, PY3, raise_with_traceback - -import base64 -import bisect -import hashlib -import array - -from future.backports import email -from future.backports.http import client as http_client -from .error import URLError, HTTPError, ContentTooShortError -from .parse import ( - urlparse, urlsplit, urljoin, unwrap, quote, unquote, - splittype, splithost, splitport, splituser, splitpasswd, - splitattr, splitquery, splitvalue, splittag, to_bytes, urlunparse) -from .response import addinfourl, addclosehook - -import io -import os -import posixpath -import re -import socket -import sys -import time -import collections -import tempfile -import contextlib -import warnings - -# check for SSL -try: - import ssl - # Not available in the SSL module in Py2: - from ssl import SSLContext -except ImportError: - _have_ssl = False -else: - _have_ssl = True - -__all__ = [ - # Classes - 'Request', 'OpenerDirector', 'BaseHandler', 'HTTPDefaultErrorHandler', - 'HTTPRedirectHandler', 'HTTPCookieProcessor', 'ProxyHandler', - 'HTTPPasswordMgr', 'HTTPPasswordMgrWithDefaultRealm', - 'AbstractBasicAuthHandler', 'HTTPBasicAuthHandler', 'ProxyBasicAuthHandler', - 'AbstractDigestAuthHandler', 'HTTPDigestAuthHandler', 'ProxyDigestAuthHandler', - 'HTTPHandler', 'FileHandler', 'FTPHandler', 'CacheFTPHandler', - 'UnknownHandler', 'HTTPErrorProcessor', - # Functions - 'urlopen', 'install_opener', 'build_opener', - 'pathname2url', 'url2pathname', 'getproxies', - # Legacy interface - 'urlretrieve', 'urlcleanup', 'URLopener', 'FancyURLopener', -] - -# used in User-Agent header sent -__version__ = sys.version[:3] - -_opener = None -def urlopen(url, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, **_3to2kwargs): - if 'cadefault' in _3to2kwargs: cadefault = _3to2kwargs['cadefault']; del _3to2kwargs['cadefault'] - else: cadefault = False - if 'capath' in _3to2kwargs: capath = _3to2kwargs['capath']; del _3to2kwargs['capath'] - else: capath = None - if 'cafile' in _3to2kwargs: cafile = _3to2kwargs['cafile']; del _3to2kwargs['cafile'] - else: cafile = None - global _opener - if cafile or capath or cadefault: - if not _have_ssl: - raise ValueError('SSL support not available') - context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) - context.options |= ssl.OP_NO_SSLv2 - context.verify_mode = ssl.CERT_REQUIRED - if cafile or capath: - context.load_verify_locations(cafile, capath) - else: - context.set_default_verify_paths() - https_handler = HTTPSHandler(context=context, check_hostname=True) - opener = build_opener(https_handler) - elif _opener is None: - _opener = opener = build_opener() - else: - opener = _opener - return opener.open(url, data, timeout) - -def install_opener(opener): - global _opener - _opener = opener - -_url_tempfiles = [] -def urlretrieve(url, filename=None, reporthook=None, data=None): - """ - Retrieve a URL into a temporary location on disk. - - Requires a URL argument. If a filename is passed, it is used as - the temporary file location. The reporthook argument should be - a callable that accepts a block number, a read size, and the - total file size of the URL target. The data argument should be - valid URL encoded data. - - If a filename is passed and the URL points to a local resource, - the result is a copy from local file to new file. - - Returns a tuple containing the path to the newly created - data file as well as the resulting HTTPMessage object. - """ - url_type, path = splittype(url) - - with contextlib.closing(urlopen(url, data)) as fp: - headers = fp.info() - - # Just return the local path and the "headers" for file:// - # URLs. No sense in performing a copy unless requested. - if url_type == "file" and not filename: - return os.path.normpath(path), headers - - # Handle temporary file setup. - if filename: - tfp = open(filename, 'wb') - else: - tfp = tempfile.NamedTemporaryFile(delete=False) - filename = tfp.name - _url_tempfiles.append(filename) - - with tfp: - result = filename, headers - bs = 1024*8 - size = -1 - read = 0 - blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) - - if reporthook: - reporthook(blocknum, bs, size) - - while True: - block = fp.read(bs) - if not block: - break - read += len(block) - tfp.write(block) - blocknum += 1 - if reporthook: - reporthook(blocknum, bs, size) - - if size >= 0 and read < size: - raise ContentTooShortError( - "retrieval incomplete: got only %i out of %i bytes" - % (read, size), result) - - return result - -def urlcleanup(): - for temp_file in _url_tempfiles: - try: - os.unlink(temp_file) - except EnvironmentError: - pass - - del _url_tempfiles[:] - global _opener - if _opener: - _opener = None - -if PY3: - _cut_port_re = re.compile(r":\d+$", re.ASCII) -else: - _cut_port_re = re.compile(r":\d+$") - -def request_host(request): - - """Return request-host, as defined by RFC 2965. - - Variation from RFC: returned value is lowercased, for convenient - comparison. - - """ - url = request.full_url - host = urlparse(url)[1] - if host == "": - host = request.get_header("Host", "") - - # remove port, if present - host = _cut_port_re.sub("", host, 1) - return host.lower() - -class Request(object): - - def __init__(self, url, data=None, headers={}, - origin_req_host=None, unverifiable=False, - method=None): - # unwrap('') --> 'type://host/path' - self.full_url = unwrap(url) - self.full_url, self.fragment = splittag(self.full_url) - self.data = data - self.headers = {} - self._tunnel_host = None - for key, value in headers.items(): - self.add_header(key, value) - self.unredirected_hdrs = {} - if origin_req_host is None: - origin_req_host = request_host(self) - self.origin_req_host = origin_req_host - self.unverifiable = unverifiable - self.method = method - self._parse() - - def _parse(self): - self.type, rest = splittype(self.full_url) - if self.type is None: - raise ValueError("unknown url type: %r" % self.full_url) - self.host, self.selector = splithost(rest) - if self.host: - self.host = unquote(self.host) - - def get_method(self): - """Return a string indicating the HTTP request method.""" - if self.method is not None: - return self.method - elif self.data is not None: - return "POST" - else: - return "GET" - - def get_full_url(self): - if self.fragment: - return '%s#%s' % (self.full_url, self.fragment) - else: - return self.full_url - - # Begin deprecated methods - - def add_data(self, data): - msg = "Request.add_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - self.data = data - - def has_data(self): - msg = "Request.has_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.data is not None - - def get_data(self): - msg = "Request.get_data method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.data - - def get_type(self): - msg = "Request.get_type method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.type - - def get_host(self): - msg = "Request.get_host method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.host - - def get_selector(self): - msg = "Request.get_selector method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.selector - - def is_unverifiable(self): - msg = "Request.is_unverifiable method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.unverifiable - - def get_origin_req_host(self): - msg = "Request.get_origin_req_host method is deprecated." - warnings.warn(msg, DeprecationWarning, stacklevel=1) - return self.origin_req_host - - # End deprecated methods - - def set_proxy(self, host, type): - if self.type == 'https' and not self._tunnel_host: - self._tunnel_host = self.host - else: - self.type= type - self.selector = self.full_url - self.host = host - - def has_proxy(self): - return self.selector == self.full_url - - def add_header(self, key, val): - # useful for something like authentication - self.headers[key.capitalize()] = val - - def add_unredirected_header(self, key, val): - # will not be added to a redirected request - self.unredirected_hdrs[key.capitalize()] = val - - def has_header(self, header_name): - return (header_name in self.headers or - header_name in self.unredirected_hdrs) - - def get_header(self, header_name, default=None): - return self.headers.get( - header_name, - self.unredirected_hdrs.get(header_name, default)) - - def header_items(self): - hdrs = self.unredirected_hdrs.copy() - hdrs.update(self.headers) - return list(hdrs.items()) - -class OpenerDirector(object): - def __init__(self): - client_version = "Python-urllib/%s" % __version__ - self.addheaders = [('User-agent', client_version)] - # self.handlers is retained only for backward compatibility - self.handlers = [] - # manage the individual handlers - self.handle_open = {} - self.handle_error = {} - self.process_response = {} - self.process_request = {} - - def add_handler(self, handler): - if not hasattr(handler, "add_parent"): - raise TypeError("expected BaseHandler instance, got %r" % - type(handler)) - - added = False - for meth in dir(handler): - if meth in ["redirect_request", "do_open", "proxy_open"]: - # oops, coincidental match - continue - - i = meth.find("_") - protocol = meth[:i] - condition = meth[i+1:] - - if condition.startswith("error"): - j = condition.find("_") + i + 1 - kind = meth[j+1:] - try: - kind = int(kind) - except ValueError: - pass - lookup = self.handle_error.get(protocol, {}) - self.handle_error[protocol] = lookup - elif condition == "open": - kind = protocol - lookup = self.handle_open - elif condition == "response": - kind = protocol - lookup = self.process_response - elif condition == "request": - kind = protocol - lookup = self.process_request - else: - continue - - handlers = lookup.setdefault(kind, []) - if handlers: - bisect.insort(handlers, handler) - else: - handlers.append(handler) - added = True - - if added: - bisect.insort(self.handlers, handler) - handler.add_parent(self) - - def close(self): - # Only exists for backwards compatibility. - pass - - def _call_chain(self, chain, kind, meth_name, *args): - # Handlers raise an exception if no one else should try to handle - # the request, or return None if they can't but another handler - # could. Otherwise, they return the response. - handlers = chain.get(kind, ()) - for handler in handlers: - func = getattr(handler, meth_name) - result = func(*args) - if result is not None: - return result - - def open(self, fullurl, data=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT): - """ - Accept a URL or a Request object - - Python-Future: if the URL is passed as a byte-string, decode it first. - """ - if isinstance(fullurl, bytes): - fullurl = fullurl.decode() - if isinstance(fullurl, str): - req = Request(fullurl, data) - else: - req = fullurl - if data is not None: - req.data = data - - req.timeout = timeout - protocol = req.type - - # pre-process request - meth_name = protocol+"_request" - for processor in self.process_request.get(protocol, []): - meth = getattr(processor, meth_name) - req = meth(req) - - response = self._open(req, data) - - # post-process response - meth_name = protocol+"_response" - for processor in self.process_response.get(protocol, []): - meth = getattr(processor, meth_name) - response = meth(req, response) - - return response - - def _open(self, req, data=None): - result = self._call_chain(self.handle_open, 'default', - 'default_open', req) - if result: - return result - - protocol = req.type - result = self._call_chain(self.handle_open, protocol, protocol + - '_open', req) - if result: - return result - - return self._call_chain(self.handle_open, 'unknown', - 'unknown_open', req) - - def error(self, proto, *args): - if proto in ('http', 'https'): - # XXX http[s] protocols are special-cased - dict = self.handle_error['http'] # https is not different than http - proto = args[2] # YUCK! - meth_name = 'http_error_%s' % proto - http_err = 1 - orig_args = args - else: - dict = self.handle_error - meth_name = proto + '_error' - http_err = 0 - args = (dict, proto, meth_name) + args - result = self._call_chain(*args) - if result: - return result - - if http_err: - args = (dict, 'default', 'http_error_default') + orig_args - return self._call_chain(*args) - -# XXX probably also want an abstract factory that knows when it makes -# sense to skip a superclass in favor of a subclass and when it might -# make sense to include both - -def build_opener(*handlers): - """Create an opener object from a list of handlers. - - The opener will use several default handlers, including support - for HTTP, FTP and when applicable HTTPS. - - If any of the handlers passed as arguments are subclasses of the - default handlers, the default handlers will not be used. - """ - def isclass(obj): - return isinstance(obj, type) or hasattr(obj, "__bases__") - - opener = OpenerDirector() - default_classes = [ProxyHandler, UnknownHandler, HTTPHandler, - HTTPDefaultErrorHandler, HTTPRedirectHandler, - FTPHandler, FileHandler, HTTPErrorProcessor] - if hasattr(http_client, "HTTPSConnection"): - default_classes.append(HTTPSHandler) - skip = set() - for klass in default_classes: - for check in handlers: - if isclass(check): - if issubclass(check, klass): - skip.add(klass) - elif isinstance(check, klass): - skip.add(klass) - for klass in skip: - default_classes.remove(klass) - - for klass in default_classes: - opener.add_handler(klass()) - - for h in handlers: - if isclass(h): - h = h() - opener.add_handler(h) - return opener - -class BaseHandler(object): - handler_order = 500 - - def add_parent(self, parent): - self.parent = parent - - def close(self): - # Only exists for backwards compatibility - pass - - def __lt__(self, other): - if not hasattr(other, "handler_order"): - # Try to preserve the old behavior of having custom classes - # inserted after default ones (works only for custom user - # classes which are not aware of handler_order). - return True - return self.handler_order < other.handler_order - - -class HTTPErrorProcessor(BaseHandler): - """Process HTTP error responses.""" - handler_order = 1000 # after all other processing - - def http_response(self, request, response): - code, msg, hdrs = response.code, response.msg, response.info() - - # According to RFC 2616, "2xx" code indicates that the client's - # request was successfully received, understood, and accepted. - if not (200 <= code < 300): - response = self.parent.error( - 'http', request, response, code, msg, hdrs) - - return response - - https_response = http_response - -class HTTPDefaultErrorHandler(BaseHandler): - def http_error_default(self, req, fp, code, msg, hdrs): - raise HTTPError(req.full_url, code, msg, hdrs, fp) - -class HTTPRedirectHandler(BaseHandler): - # maximum number of redirections to any single URL - # this is needed because of the state that cookies introduce - max_repeats = 4 - # maximum total number of redirections (regardless of URL) before - # assuming we're in a loop - max_redirections = 10 - - def redirect_request(self, req, fp, code, msg, headers, newurl): - """Return a Request or None in response to a redirect. - - This is called by the http_error_30x methods when a - redirection response is received. If a redirection should - take place, return a new Request to allow http_error_30x to - perform the redirect. Otherwise, raise HTTPError if no-one - else should try to handle this url. Return None if you can't - but another Handler might. - """ - m = req.get_method() - if (not (code in (301, 302, 303, 307) and m in ("GET", "HEAD") - or code in (301, 302, 303) and m == "POST")): - raise HTTPError(req.full_url, code, msg, headers, fp) - - # Strictly (according to RFC 2616), 301 or 302 in response to - # a POST MUST NOT cause a redirection without confirmation - # from the user (of urllib.request, in this case). In practice, - # essentially all clients do redirect in this case, so we do - # the same. - # be conciliant with URIs containing a space - newurl = newurl.replace(' ', '%20') - CONTENT_HEADERS = ("content-length", "content-type") - newheaders = dict((k, v) for k, v in req.headers.items() - if k.lower() not in CONTENT_HEADERS) - return Request(newurl, - headers=newheaders, - origin_req_host=req.origin_req_host, - unverifiable=True) - - # Implementation note: To avoid the server sending us into an - # infinite loop, the request object needs to track what URLs we - # have already seen. Do this by adding a handler-specific - # attribute to the Request object. - def http_error_302(self, req, fp, code, msg, headers): - # Some servers (incorrectly) return multiple Location headers - # (so probably same goes for URI). Use first header. - if "location" in headers: - newurl = headers["location"] - elif "uri" in headers: - newurl = headers["uri"] - else: - return - - # fix a possible malformed URL - urlparts = urlparse(newurl) - - # For security reasons we don't allow redirection to anything other - # than http, https or ftp. - - if urlparts.scheme not in ('http', 'https', 'ftp', ''): - raise HTTPError( - newurl, code, - "%s - Redirection to url '%s' is not allowed" % (msg, newurl), - headers, fp) - - if not urlparts.path: - urlparts = list(urlparts) - urlparts[2] = "/" - newurl = urlunparse(urlparts) - - newurl = urljoin(req.full_url, newurl) - - # XXX Probably want to forget about the state of the current - # request, although that might interact poorly with other - # handlers that also use handler-specific request attributes - new = self.redirect_request(req, fp, code, msg, headers, newurl) - if new is None: - return - - # loop detection - # .redirect_dict has a key url if url was previously visited. - if hasattr(req, 'redirect_dict'): - visited = new.redirect_dict = req.redirect_dict - if (visited.get(newurl, 0) >= self.max_repeats or - len(visited) >= self.max_redirections): - raise HTTPError(req.full_url, code, - self.inf_msg + msg, headers, fp) - else: - visited = new.redirect_dict = req.redirect_dict = {} - visited[newurl] = visited.get(newurl, 0) + 1 - - # Don't close the fp until we are sure that we won't use it - # with HTTPError. - fp.read() - fp.close() - - return self.parent.open(new, timeout=req.timeout) - - http_error_301 = http_error_303 = http_error_307 = http_error_302 - - inf_msg = "The HTTP server returned a redirect error that would " \ - "lead to an infinite loop.\n" \ - "The last 30x error message was:\n" - - -def _parse_proxy(proxy): - """Return (scheme, user, password, host/port) given a URL or an authority. - - If a URL is supplied, it must have an authority (host:port) component. - According to RFC 3986, having an authority component means the URL must - have two slashes after the scheme: - - >>> _parse_proxy('file:/ftp.example.com/') - Traceback (most recent call last): - ValueError: proxy URL with no authority: 'file:/ftp.example.com/' - - The first three items of the returned tuple may be None. - - Examples of authority parsing: - - >>> _parse_proxy('proxy.example.com') - (None, None, None, 'proxy.example.com') - >>> _parse_proxy('proxy.example.com:3128') - (None, None, None, 'proxy.example.com:3128') - - The authority component may optionally include userinfo (assumed to be - username:password): - - >>> _parse_proxy('joe:password@proxy.example.com') - (None, 'joe', 'password', 'proxy.example.com') - >>> _parse_proxy('joe:password@proxy.example.com:3128') - (None, 'joe', 'password', 'proxy.example.com:3128') - - Same examples, but with URLs instead: - - >>> _parse_proxy('http://proxy.example.com/') - ('http', None, None, 'proxy.example.com') - >>> _parse_proxy('http://proxy.example.com:3128/') - ('http', None, None, 'proxy.example.com:3128') - >>> _parse_proxy('http://joe:password@proxy.example.com/') - ('http', 'joe', 'password', 'proxy.example.com') - >>> _parse_proxy('http://joe:password@proxy.example.com:3128') - ('http', 'joe', 'password', 'proxy.example.com:3128') - - Everything after the authority is ignored: - - >>> _parse_proxy('ftp://joe:password@proxy.example.com/rubbish:3128') - ('ftp', 'joe', 'password', 'proxy.example.com') - - Test for no trailing '/' case: - - >>> _parse_proxy('http://joe:password@proxy.example.com') - ('http', 'joe', 'password', 'proxy.example.com') - - """ - scheme, r_scheme = splittype(proxy) - if not r_scheme.startswith("/"): - # authority - scheme = None - authority = proxy - else: - # URL - if not r_scheme.startswith("//"): - raise ValueError("proxy URL with no authority: %r" % proxy) - # We have an authority, so for RFC 3986-compliant URLs (by ss 3. - # and 3.3.), path is empty or starts with '/' - end = r_scheme.find("/", 2) - if end == -1: - end = None - authority = r_scheme[2:end] - userinfo, hostport = splituser(authority) - if userinfo is not None: - user, password = splitpasswd(userinfo) - else: - user = password = None - return scheme, user, password, hostport - -class ProxyHandler(BaseHandler): - # Proxies must be in front - handler_order = 100 - - def __init__(self, proxies=None): - if proxies is None: - proxies = getproxies() - assert hasattr(proxies, 'keys'), "proxies must be a mapping" - self.proxies = proxies - for type, url in proxies.items(): - setattr(self, '%s_open' % type, - lambda r, proxy=url, type=type, meth=self.proxy_open: - meth(r, proxy, type)) - - def proxy_open(self, req, proxy, type): - orig_type = req.type - proxy_type, user, password, hostport = _parse_proxy(proxy) - if proxy_type is None: - proxy_type = orig_type - - if req.host and proxy_bypass(req.host): - return None - - if user and password: - user_pass = '%s:%s' % (unquote(user), - unquote(password)) - creds = base64.b64encode(user_pass.encode()).decode("ascii") - req.add_header('Proxy-authorization', 'Basic ' + creds) - hostport = unquote(hostport) - req.set_proxy(hostport, proxy_type) - if orig_type == proxy_type or orig_type == 'https': - # let other handlers take care of it - return None - else: - # need to start over, because the other handlers don't - # grok the proxy's URL type - # e.g. if we have a constructor arg proxies like so: - # {'http': 'ftp://proxy.example.com'}, we may end up turning - # a request for http://acme.example.com/a into one for - # ftp://proxy.example.com/a - return self.parent.open(req, timeout=req.timeout) - -class HTTPPasswordMgr(object): - - def __init__(self): - self.passwd = {} - - def add_password(self, realm, uri, user, passwd): - # uri could be a single URI or a sequence - if isinstance(uri, str): - uri = [uri] - if realm not in self.passwd: - self.passwd[realm] = {} - for default_port in True, False: - reduced_uri = tuple( - [self.reduce_uri(u, default_port) for u in uri]) - self.passwd[realm][reduced_uri] = (user, passwd) - - def find_user_password(self, realm, authuri): - domains = self.passwd.get(realm, {}) - for default_port in True, False: - reduced_authuri = self.reduce_uri(authuri, default_port) - for uris, authinfo in domains.items(): - for uri in uris: - if self.is_suburi(uri, reduced_authuri): - return authinfo - return None, None - - def reduce_uri(self, uri, default_port=True): - """Accept authority or URI and extract only the authority and path.""" - # note HTTP URLs do not have a userinfo component - parts = urlsplit(uri) - if parts[1]: - # URI - scheme = parts[0] - authority = parts[1] - path = parts[2] or '/' - else: - # host or host:port - scheme = None - authority = uri - path = '/' - host, port = splitport(authority) - if default_port and port is None and scheme is not None: - dport = {"http": 80, - "https": 443, - }.get(scheme) - if dport is not None: - authority = "%s:%d" % (host, dport) - return authority, path - - def is_suburi(self, base, test): - """Check if test is below base in a URI tree - - Both args must be URIs in reduced form. - """ - if base == test: - return True - if base[0] != test[0]: - return False - common = posixpath.commonprefix((base[1], test[1])) - if len(common) == len(base[1]): - return True - return False - - -class HTTPPasswordMgrWithDefaultRealm(HTTPPasswordMgr): - - def find_user_password(self, realm, authuri): - user, password = HTTPPasswordMgr.find_user_password(self, realm, - authuri) - if user is not None: - return user, password - return HTTPPasswordMgr.find_user_password(self, None, authuri) - - -class AbstractBasicAuthHandler(object): - - # XXX this allows for multiple auth-schemes, but will stupidly pick - # the last one with a realm specified. - - # allow for double- and single-quoted realm values - # (single quotes are a violation of the RFC, but appear in the wild) - rx = re.compile('(?:.*,)*[ \t]*([^ \t]+)[ \t]+' - 'realm=(["\']?)([^"\']*)\\2', re.I) - - # XXX could pre-emptively send auth info already accepted (RFC 2617, - # end of section 2, and section 1.2 immediately after "credentials" - # production). - - def __init__(self, password_mgr=None): - if password_mgr is None: - password_mgr = HTTPPasswordMgr() - self.passwd = password_mgr - self.add_password = self.passwd.add_password - self.retried = 0 - - def reset_retry_count(self): - self.retried = 0 - - def http_error_auth_reqed(self, authreq, host, req, headers): - # host may be an authority (without userinfo) or a URL with an - # authority - # XXX could be multiple headers - authreq = headers.get(authreq, None) - - if self.retried > 5: - # retry sending the username:password 5 times before failing. - raise HTTPError(req.get_full_url(), 401, "basic auth failed", - headers, None) - else: - self.retried += 1 - - if authreq: - scheme = authreq.split()[0] - if scheme.lower() != 'basic': - raise ValueError("AbstractBasicAuthHandler does not" - " support the following scheme: '%s'" % - scheme) - else: - mo = AbstractBasicAuthHandler.rx.search(authreq) - if mo: - scheme, quote, realm = mo.groups() - if quote not in ['"',"'"]: - warnings.warn("Basic Auth Realm was unquoted", - UserWarning, 2) - if scheme.lower() == 'basic': - response = self.retry_http_basic_auth(host, req, realm) - if response and response.code != 401: - self.retried = 0 - return response - - def retry_http_basic_auth(self, host, req, realm): - user, pw = self.passwd.find_user_password(realm, host) - if pw is not None: - raw = "%s:%s" % (user, pw) - auth = "Basic " + base64.b64encode(raw.encode()).decode("ascii") - if req.headers.get(self.auth_header, None) == auth: - return None - req.add_unredirected_header(self.auth_header, auth) - return self.parent.open(req, timeout=req.timeout) - else: - return None - - -class HTTPBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler): - - auth_header = 'Authorization' - - def http_error_401(self, req, fp, code, msg, headers): - url = req.full_url - response = self.http_error_auth_reqed('www-authenticate', - url, req, headers) - self.reset_retry_count() - return response - - -class ProxyBasicAuthHandler(AbstractBasicAuthHandler, BaseHandler): - - auth_header = 'Proxy-authorization' - - def http_error_407(self, req, fp, code, msg, headers): - # http_error_auth_reqed requires that there is no userinfo component in - # authority. Assume there isn't one, since urllib.request does not (and - # should not, RFC 3986 s. 3.2.1) support requests for URLs containing - # userinfo. - authority = req.host - response = self.http_error_auth_reqed('proxy-authenticate', - authority, req, headers) - self.reset_retry_count() - return response - - -# Return n random bytes. -_randombytes = os.urandom - - -class AbstractDigestAuthHandler(object): - # Digest authentication is specified in RFC 2617. - - # XXX The client does not inspect the Authentication-Info header - # in a successful response. - - # XXX It should be possible to test this implementation against - # a mock server that just generates a static set of challenges. - - # XXX qop="auth-int" supports is shaky - - def __init__(self, passwd=None): - if passwd is None: - passwd = HTTPPasswordMgr() - self.passwd = passwd - self.add_password = self.passwd.add_password - self.retried = 0 - self.nonce_count = 0 - self.last_nonce = None - - def reset_retry_count(self): - self.retried = 0 - - def http_error_auth_reqed(self, auth_header, host, req, headers): - authreq = headers.get(auth_header, None) - if self.retried > 5: - # Don't fail endlessly - if we failed once, we'll probably - # fail a second time. Hm. Unless the Password Manager is - # prompting for the information. Crap. This isn't great - # but it's better than the current 'repeat until recursion - # depth exceeded' approach - raise HTTPError(req.full_url, 401, "digest auth failed", - headers, None) - else: - self.retried += 1 - if authreq: - scheme = authreq.split()[0] - if scheme.lower() == 'digest': - return self.retry_http_digest_auth(req, authreq) - elif scheme.lower() != 'basic': - raise ValueError("AbstractDigestAuthHandler does not support" - " the following scheme: '%s'" % scheme) - - def retry_http_digest_auth(self, req, auth): - token, challenge = auth.split(' ', 1) - chal = parse_keqv_list(filter(None, parse_http_list(challenge))) - auth = self.get_authorization(req, chal) - if auth: - auth_val = 'Digest %s' % auth - if req.headers.get(self.auth_header, None) == auth_val: - return None - req.add_unredirected_header(self.auth_header, auth_val) - resp = self.parent.open(req, timeout=req.timeout) - return resp - - def get_cnonce(self, nonce): - # The cnonce-value is an opaque - # quoted string value provided by the client and used by both client - # and server to avoid chosen plaintext attacks, to provide mutual - # authentication, and to provide some message integrity protection. - # This isn't a fabulous effort, but it's probably Good Enough. - s = "%s:%s:%s:" % (self.nonce_count, nonce, time.ctime()) - b = s.encode("ascii") + _randombytes(8) - dig = hashlib.sha1(b).hexdigest() - return dig[:16] - - def get_authorization(self, req, chal): - try: - realm = chal['realm'] - nonce = chal['nonce'] - qop = chal.get('qop') - algorithm = chal.get('algorithm', 'MD5') - # mod_digest doesn't send an opaque, even though it isn't - # supposed to be optional - opaque = chal.get('opaque', None) - except KeyError: - return None - - H, KD = self.get_algorithm_impls(algorithm) - if H is None: - return None - - user, pw = self.passwd.find_user_password(realm, req.full_url) - if user is None: - return None - - # XXX not implemented yet - if req.data is not None: - entdig = self.get_entity_digest(req.data, chal) - else: - entdig = None - - A1 = "%s:%s:%s" % (user, realm, pw) - A2 = "%s:%s" % (req.get_method(), - # XXX selector: what about proxies and full urls - req.selector) - if qop == 'auth': - if nonce == self.last_nonce: - self.nonce_count += 1 - else: - self.nonce_count = 1 - self.last_nonce = nonce - ncvalue = '%08x' % self.nonce_count - cnonce = self.get_cnonce(nonce) - noncebit = "%s:%s:%s:%s:%s" % (nonce, ncvalue, cnonce, qop, H(A2)) - respdig = KD(H(A1), noncebit) - elif qop is None: - respdig = KD(H(A1), "%s:%s" % (nonce, H(A2))) - else: - # XXX handle auth-int. - raise URLError("qop '%s' is not supported." % qop) - - # XXX should the partial digests be encoded too? - - base = 'username="%s", realm="%s", nonce="%s", uri="%s", ' \ - 'response="%s"' % (user, realm, nonce, req.selector, - respdig) - if opaque: - base += ', opaque="%s"' % opaque - if entdig: - base += ', digest="%s"' % entdig - base += ', algorithm="%s"' % algorithm - if qop: - base += ', qop=auth, nc=%s, cnonce="%s"' % (ncvalue, cnonce) - return base - - def get_algorithm_impls(self, algorithm): - # lambdas assume digest modules are imported at the top level - if algorithm == 'MD5': - H = lambda x: hashlib.md5(x.encode("ascii")).hexdigest() - elif algorithm == 'SHA': - H = lambda x: hashlib.sha1(x.encode("ascii")).hexdigest() - # XXX MD5-sess - KD = lambda s, d: H("%s:%s" % (s, d)) - return H, KD - - def get_entity_digest(self, data, chal): - # XXX not implemented yet - return None - - -class HTTPDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): - """An authentication protocol defined by RFC 2069 - - Digest authentication improves on basic authentication because it - does not transmit passwords in the clear. - """ - - auth_header = 'Authorization' - handler_order = 490 # before Basic auth - - def http_error_401(self, req, fp, code, msg, headers): - host = urlparse(req.full_url)[1] - retry = self.http_error_auth_reqed('www-authenticate', - host, req, headers) - self.reset_retry_count() - return retry - - -class ProxyDigestAuthHandler(BaseHandler, AbstractDigestAuthHandler): - - auth_header = 'Proxy-Authorization' - handler_order = 490 # before Basic auth - - def http_error_407(self, req, fp, code, msg, headers): - host = req.host - retry = self.http_error_auth_reqed('proxy-authenticate', - host, req, headers) - self.reset_retry_count() - return retry - -class AbstractHTTPHandler(BaseHandler): - - def __init__(self, debuglevel=0): - self._debuglevel = debuglevel - - def set_http_debuglevel(self, level): - self._debuglevel = level - - def do_request_(self, request): - host = request.host - if not host: - raise URLError('no host given') - - if request.data is not None: # POST - data = request.data - if isinstance(data, str): - msg = "POST data should be bytes or an iterable of bytes. " \ - "It cannot be of type str." - raise TypeError(msg) - if not request.has_header('Content-type'): - request.add_unredirected_header( - 'Content-type', - 'application/x-www-form-urlencoded') - if not request.has_header('Content-length'): - size = None - try: - ### For Python-Future: - if PY2 and isinstance(data, array.array): - # memoryviews of arrays aren't supported - # in Py2.7. (e.g. memoryview(array.array('I', - # [1, 2, 3, 4])) raises a TypeError.) - # So we calculate the size manually instead: - size = len(data) * data.itemsize - ### - else: - mv = memoryview(data) - size = len(mv) * mv.itemsize - except TypeError: - if isinstance(data, collections.Iterable): - raise ValueError("Content-Length should be specified " - "for iterable data of type %r %r" % (type(data), - data)) - else: - request.add_unredirected_header( - 'Content-length', '%d' % size) - - sel_host = host - if request.has_proxy(): - scheme, sel = splittype(request.selector) - sel_host, sel_path = splithost(sel) - if not request.has_header('Host'): - request.add_unredirected_header('Host', sel_host) - for name, value in self.parent.addheaders: - name = name.capitalize() - if not request.has_header(name): - request.add_unredirected_header(name, value) - - return request - - def do_open(self, http_class, req, **http_conn_args): - """Return an HTTPResponse object for the request, using http_class. - - http_class must implement the HTTPConnection API from http.client. - """ - host = req.host - if not host: - raise URLError('no host given') - - # will parse host:port - h = http_class(host, timeout=req.timeout, **http_conn_args) - - headers = dict(req.unredirected_hdrs) - headers.update(dict((k, v) for k, v in req.headers.items() - if k not in headers)) - - # TODO(jhylton): Should this be redesigned to handle - # persistent connections? - - # We want to make an HTTP/1.1 request, but the addinfourl - # class isn't prepared to deal with a persistent connection. - # It will try to read all remaining data from the socket, - # which will block while the server waits for the next request. - # So make sure the connection gets closed after the (only) - # request. - headers["Connection"] = "close" - headers = dict((name.title(), val) for name, val in headers.items()) - - if req._tunnel_host: - tunnel_headers = {} - proxy_auth_hdr = "Proxy-Authorization" - if proxy_auth_hdr in headers: - tunnel_headers[proxy_auth_hdr] = headers[proxy_auth_hdr] - # Proxy-Authorization should not be sent to origin - # server. - del headers[proxy_auth_hdr] - h.set_tunnel(req._tunnel_host, headers=tunnel_headers) - - try: - h.request(req.get_method(), req.selector, req.data, headers) - except socket.error as err: # timeout error - h.close() - raise URLError(err) - else: - r = h.getresponse() - # If the server does not send us a 'Connection: close' header, - # HTTPConnection assumes the socket should be left open. Manually - # mark the socket to be closed when this response object goes away. - if h.sock: - h.sock.close() - h.sock = None - - - r.url = req.get_full_url() - # This line replaces the .msg attribute of the HTTPResponse - # with .headers, because urllib clients expect the response to - # have the reason in .msg. It would be good to mark this - # attribute is deprecated and get then to use info() or - # .headers. - r.msg = r.reason - return r - - -class HTTPHandler(AbstractHTTPHandler): - - def http_open(self, req): - return self.do_open(http_client.HTTPConnection, req) - - http_request = AbstractHTTPHandler.do_request_ - -if hasattr(http_client, 'HTTPSConnection'): - - class HTTPSHandler(AbstractHTTPHandler): - - def __init__(self, debuglevel=0, context=None, check_hostname=None): - AbstractHTTPHandler.__init__(self, debuglevel) - self._context = context - self._check_hostname = check_hostname - - def https_open(self, req): - return self.do_open(http_client.HTTPSConnection, req, - context=self._context, check_hostname=self._check_hostname) - - https_request = AbstractHTTPHandler.do_request_ - - __all__.append('HTTPSHandler') - -class HTTPCookieProcessor(BaseHandler): - def __init__(self, cookiejar=None): - import future.backports.http.cookiejar as http_cookiejar - if cookiejar is None: - cookiejar = http_cookiejar.CookieJar() - self.cookiejar = cookiejar - - def http_request(self, request): - self.cookiejar.add_cookie_header(request) - return request - - def http_response(self, request, response): - self.cookiejar.extract_cookies(response, request) - return response - - https_request = http_request - https_response = http_response - -class UnknownHandler(BaseHandler): - def unknown_open(self, req): - type = req.type - raise URLError('unknown url type: %s' % type) - -def parse_keqv_list(l): - """Parse list of key=value strings where keys are not duplicated.""" - parsed = {} - for elt in l: - k, v = elt.split('=', 1) - if v[0] == '"' and v[-1] == '"': - v = v[1:-1] - parsed[k] = v - return parsed - -def parse_http_list(s): - """Parse lists as described by RFC 2068 Section 2. - - In particular, parse comma-separated lists where the elements of - the list may include quoted-strings. A quoted-string could - contain a comma. A non-quoted string could have quotes in the - middle. Neither commas nor quotes count if they are escaped. - Only double-quotes count, not single-quotes. - """ - res = [] - part = '' - - escape = quote = False - for cur in s: - if escape: - part += cur - escape = False - continue - if quote: - if cur == '\\': - escape = True - continue - elif cur == '"': - quote = False - part += cur - continue - - if cur == ',': - res.append(part) - part = '' - continue - - if cur == '"': - quote = True - - part += cur - - # append last part - if part: - res.append(part) - - return [part.strip() for part in res] - -class FileHandler(BaseHandler): - # Use local file or FTP depending on form of URL - def file_open(self, req): - url = req.selector - if url[:2] == '//' and url[2:3] != '/' and (req.host and - req.host != 'localhost'): - if not req.host is self.get_names(): - raise URLError("file:// scheme is supported only on localhost") - else: - return self.open_local_file(req) - - # names for the localhost - names = None - def get_names(self): - if FileHandler.names is None: - try: - FileHandler.names = tuple( - socket.gethostbyname_ex('localhost')[2] + - socket.gethostbyname_ex(socket.gethostname())[2]) - except socket.gaierror: - FileHandler.names = (socket.gethostbyname('localhost'),) - return FileHandler.names - - # not entirely sure what the rules are here - def open_local_file(self, req): - import future.backports.email.utils as email_utils - import mimetypes - host = req.host - filename = req.selector - localfile = url2pathname(filename) - try: - stats = os.stat(localfile) - size = stats.st_size - modified = email_utils.formatdate(stats.st_mtime, usegmt=True) - mtype = mimetypes.guess_type(filename)[0] - headers = email.message_from_string( - 'Content-type: %s\nContent-length: %d\nLast-modified: %s\n' % - (mtype or 'text/plain', size, modified)) - if host: - host, port = splitport(host) - if not host or \ - (not port and _safe_gethostbyname(host) in self.get_names()): - if host: - origurl = 'file://' + host + filename - else: - origurl = 'file://' + filename - return addinfourl(open(localfile, 'rb'), headers, origurl) - except OSError as exp: - # users shouldn't expect OSErrors coming from urlopen() - raise URLError(exp) - raise URLError('file not on local host') - -def _safe_gethostbyname(host): - try: - return socket.gethostbyname(host) - except socket.gaierror: - return None - -class FTPHandler(BaseHandler): - def ftp_open(self, req): - import ftplib - import mimetypes - host = req.host - if not host: - raise URLError('ftp error: no host given') - host, port = splitport(host) - if port is None: - port = ftplib.FTP_PORT - else: - port = int(port) - - # username/password handling - user, host = splituser(host) - if user: - user, passwd = splitpasswd(user) - else: - passwd = None - host = unquote(host) - user = user or '' - passwd = passwd or '' - - try: - host = socket.gethostbyname(host) - except socket.error as msg: - raise URLError(msg) - path, attrs = splitattr(req.selector) - dirs = path.split('/') - dirs = list(map(unquote, dirs)) - dirs, file = dirs[:-1], dirs[-1] - if dirs and not dirs[0]: - dirs = dirs[1:] - try: - fw = self.connect_ftp(user, passwd, host, port, dirs, req.timeout) - type = file and 'I' or 'D' - for attr in attrs: - attr, value = splitvalue(attr) - if attr.lower() == 'type' and \ - value in ('a', 'A', 'i', 'I', 'd', 'D'): - type = value.upper() - fp, retrlen = fw.retrfile(file, type) - headers = "" - mtype = mimetypes.guess_type(req.full_url)[0] - if mtype: - headers += "Content-type: %s\n" % mtype - if retrlen is not None and retrlen >= 0: - headers += "Content-length: %d\n" % retrlen - headers = email.message_from_string(headers) - return addinfourl(fp, headers, req.full_url) - except ftplib.all_errors as exp: - exc = URLError('ftp error: %r' % exp) - raise_with_traceback(exc) - - def connect_ftp(self, user, passwd, host, port, dirs, timeout): - return ftpwrapper(user, passwd, host, port, dirs, timeout, - persistent=False) - -class CacheFTPHandler(FTPHandler): - # XXX would be nice to have pluggable cache strategies - # XXX this stuff is definitely not thread safe - def __init__(self): - self.cache = {} - self.timeout = {} - self.soonest = 0 - self.delay = 60 - self.max_conns = 16 - - def setTimeout(self, t): - self.delay = t - - def setMaxConns(self, m): - self.max_conns = m - - def connect_ftp(self, user, passwd, host, port, dirs, timeout): - key = user, host, port, '/'.join(dirs), timeout - if key in self.cache: - self.timeout[key] = time.time() + self.delay - else: - self.cache[key] = ftpwrapper(user, passwd, host, port, - dirs, timeout) - self.timeout[key] = time.time() + self.delay - self.check_cache() - return self.cache[key] - - def check_cache(self): - # first check for old ones - t = time.time() - if self.soonest <= t: - for k, v in list(self.timeout.items()): - if v < t: - self.cache[k].close() - del self.cache[k] - del self.timeout[k] - self.soonest = min(list(self.timeout.values())) - - # then check the size - if len(self.cache) == self.max_conns: - for k, v in list(self.timeout.items()): - if v == self.soonest: - del self.cache[k] - del self.timeout[k] - break - self.soonest = min(list(self.timeout.values())) - - def clear_cache(self): - for conn in self.cache.values(): - conn.close() - self.cache.clear() - self.timeout.clear() - - -# Code move from the old urllib module - -MAXFTPCACHE = 10 # Trim the ftp cache beyond this size - -# Helper for non-unix systems -if os.name == 'nt': - from nturl2path import url2pathname, pathname2url -else: - def url2pathname(pathname): - """OS-specific conversion from a relative URL of the 'file' scheme - to a file system path; not recommended for general use.""" - return unquote(pathname) - - def pathname2url(pathname): - """OS-specific conversion from a file system path to a relative URL - of the 'file' scheme; not recommended for general use.""" - return quote(pathname) - -# This really consists of two pieces: -# (1) a class which handles opening of all sorts of URLs -# (plus assorted utilities etc.) -# (2) a set of functions for parsing URLs -# XXX Should these be separated out into different modules? - - -ftpcache = {} -class URLopener(object): - """Class to open URLs. - This is a class rather than just a subroutine because we may need - more than one set of global protocol-specific options. - Note -- this is a base class for those who don't want the - automatic handling of errors type 302 (relocated) and 401 - (authorization needed).""" - - __tempfiles = None - - version = "Python-urllib/%s" % __version__ - - # Constructor - def __init__(self, proxies=None, **x509): - msg = "%(class)s style of invoking requests is deprecated. " \ - "Use newer urlopen functions/methods" % {'class': self.__class__.__name__} - warnings.warn(msg, DeprecationWarning, stacklevel=3) - if proxies is None: - proxies = getproxies() - assert hasattr(proxies, 'keys'), "proxies must be a mapping" - self.proxies = proxies - self.key_file = x509.get('key_file') - self.cert_file = x509.get('cert_file') - self.addheaders = [('User-Agent', self.version)] - self.__tempfiles = [] - self.__unlink = os.unlink # See cleanup() - self.tempcache = None - # Undocumented feature: if you assign {} to tempcache, - # it is used to cache files retrieved with - # self.retrieve(). This is not enabled by default - # since it does not work for changing documents (and I - # haven't got the logic to check expiration headers - # yet). - self.ftpcache = ftpcache - # Undocumented feature: you can use a different - # ftp cache by assigning to the .ftpcache member; - # in case you want logically independent URL openers - # XXX This is not threadsafe. Bah. - - def __del__(self): - self.close() - - def close(self): - self.cleanup() - - def cleanup(self): - # This code sometimes runs when the rest of this module - # has already been deleted, so it can't use any globals - # or import anything. - if self.__tempfiles: - for file in self.__tempfiles: - try: - self.__unlink(file) - except OSError: - pass - del self.__tempfiles[:] - if self.tempcache: - self.tempcache.clear() - - def addheader(self, *args): - """Add a header to be used by the HTTP interface only - e.g. u.addheader('Accept', 'sound/basic')""" - self.addheaders.append(args) - - # External interface - def open(self, fullurl, data=None): - """Use URLopener().open(file) instead of open(file, 'r').""" - fullurl = unwrap(to_bytes(fullurl)) - fullurl = quote(fullurl, safe="%/:=&?~#+!$,;'@()*[]|") - if self.tempcache and fullurl in self.tempcache: - filename, headers = self.tempcache[fullurl] - fp = open(filename, 'rb') - return addinfourl(fp, headers, fullurl) - urltype, url = splittype(fullurl) - if not urltype: - urltype = 'file' - if urltype in self.proxies: - proxy = self.proxies[urltype] - urltype, proxyhost = splittype(proxy) - host, selector = splithost(proxyhost) - url = (host, fullurl) # Signal special case to open_*() - else: - proxy = None - name = 'open_' + urltype - self.type = urltype - name = name.replace('-', '_') - if not hasattr(self, name): - if proxy: - return self.open_unknown_proxy(proxy, fullurl, data) - else: - return self.open_unknown(fullurl, data) - try: - if data is None: - return getattr(self, name)(url) - else: - return getattr(self, name)(url, data) - except HTTPError: - raise - except socket.error as msg: - raise_with_traceback(IOError('socket error', msg)) - - def open_unknown(self, fullurl, data=None): - """Overridable interface to open unknown URL type.""" - type, url = splittype(fullurl) - raise IOError('url error', 'unknown url type', type) - - def open_unknown_proxy(self, proxy, fullurl, data=None): - """Overridable interface to open unknown URL type.""" - type, url = splittype(fullurl) - raise IOError('url error', 'invalid proxy for %s' % type, proxy) - - # External interface - def retrieve(self, url, filename=None, reporthook=None, data=None): - """retrieve(url) returns (filename, headers) for a local object - or (tempfilename, headers) for a remote object.""" - url = unwrap(to_bytes(url)) - if self.tempcache and url in self.tempcache: - return self.tempcache[url] - type, url1 = splittype(url) - if filename is None and (not type or type == 'file'): - try: - fp = self.open_local_file(url1) - hdrs = fp.info() - fp.close() - return url2pathname(splithost(url1)[1]), hdrs - except IOError as msg: - pass - fp = self.open(url, data) - try: - headers = fp.info() - if filename: - tfp = open(filename, 'wb') - else: - import tempfile - garbage, path = splittype(url) - garbage, path = splithost(path or "") - path, garbage = splitquery(path or "") - path, garbage = splitattr(path or "") - suffix = os.path.splitext(path)[1] - (fd, filename) = tempfile.mkstemp(suffix) - self.__tempfiles.append(filename) - tfp = os.fdopen(fd, 'wb') - try: - result = filename, headers - if self.tempcache is not None: - self.tempcache[url] = result - bs = 1024*8 - size = -1 - read = 0 - blocknum = 0 - if "content-length" in headers: - size = int(headers["Content-Length"]) - if reporthook: - reporthook(blocknum, bs, size) - while 1: - block = fp.read(bs) - if not block: - break - read += len(block) - tfp.write(block) - blocknum += 1 - if reporthook: - reporthook(blocknum, bs, size) - finally: - tfp.close() - finally: - fp.close() - - # raise exception if actual size does not match content-length header - if size >= 0 and read < size: - raise ContentTooShortError( - "retrieval incomplete: got only %i out of %i bytes" - % (read, size), result) - - return result - - # Each method named open_ knows how to open that type of URL - - def _open_generic_http(self, connection_factory, url, data): - """Make an HTTP connection using connection_class. - - This is an internal method that should be called from - open_http() or open_https(). - - Arguments: - - connection_factory should take a host name and return an - HTTPConnection instance. - - url is the url to retrieval or a host, relative-path pair. - - data is payload for a POST request or None. - """ - - user_passwd = None - proxy_passwd= None - if isinstance(url, str): - host, selector = splithost(url) - if host: - user_passwd, host = splituser(host) - host = unquote(host) - realhost = host - else: - host, selector = url - # check whether the proxy contains authorization information - proxy_passwd, host = splituser(host) - # now we proceed with the url we want to obtain - urltype, rest = splittype(selector) - url = rest - user_passwd = None - if urltype.lower() != 'http': - realhost = None - else: - realhost, rest = splithost(rest) - if realhost: - user_passwd, realhost = splituser(realhost) - if user_passwd: - selector = "%s://%s%s" % (urltype, realhost, rest) - if proxy_bypass(realhost): - host = realhost - - if not host: raise IOError('http error', 'no host given') - - if proxy_passwd: - proxy_passwd = unquote(proxy_passwd) - proxy_auth = base64.b64encode(proxy_passwd.encode()).decode('ascii') - else: - proxy_auth = None - - if user_passwd: - user_passwd = unquote(user_passwd) - auth = base64.b64encode(user_passwd.encode()).decode('ascii') - else: - auth = None - http_conn = connection_factory(host) - headers = {} - if proxy_auth: - headers["Proxy-Authorization"] = "Basic %s" % proxy_auth - if auth: - headers["Authorization"] = "Basic %s" % auth - if realhost: - headers["Host"] = realhost - - # Add Connection:close as we don't support persistent connections yet. - # This helps in closing the socket and avoiding ResourceWarning - - headers["Connection"] = "close" - - for header, value in self.addheaders: - headers[header] = value - - if data is not None: - headers["Content-Type"] = "application/x-www-form-urlencoded" - http_conn.request("POST", selector, data, headers) - else: - http_conn.request("GET", selector, headers=headers) - - try: - response = http_conn.getresponse() - except http_client.BadStatusLine: - # something went wrong with the HTTP status line - raise URLError("http protocol error: bad status line") - - # According to RFC 2616, "2xx" code indicates that the client's - # request was successfully received, understood, and accepted. - if 200 <= response.status < 300: - return addinfourl(response, response.msg, "http:" + url, - response.status) - else: - return self.http_error( - url, response.fp, - response.status, response.reason, response.msg, data) - - def open_http(self, url, data=None): - """Use HTTP protocol.""" - return self._open_generic_http(http_client.HTTPConnection, url, data) - - def http_error(self, url, fp, errcode, errmsg, headers, data=None): - """Handle http errors. - - Derived class can override this, or provide specific handlers - named http_error_DDD where DDD is the 3-digit error code.""" - # First check if there's a specific handler for this error - name = 'http_error_%d' % errcode - if hasattr(self, name): - method = getattr(self, name) - if data is None: - result = method(url, fp, errcode, errmsg, headers) - else: - result = method(url, fp, errcode, errmsg, headers, data) - if result: return result - return self.http_error_default(url, fp, errcode, errmsg, headers) - - def http_error_default(self, url, fp, errcode, errmsg, headers): - """Default error handler: close the connection and raise IOError.""" - fp.close() - raise HTTPError(url, errcode, errmsg, headers, None) - - if _have_ssl: - def _https_connection(self, host): - return http_client.HTTPSConnection(host, - key_file=self.key_file, - cert_file=self.cert_file) - - def open_https(self, url, data=None): - """Use HTTPS protocol.""" - return self._open_generic_http(self._https_connection, url, data) - - def open_file(self, url): - """Use local file or FTP depending on form of URL.""" - if not isinstance(url, str): - raise URLError('file error: proxy support for file protocol currently not implemented') - if url[:2] == '//' and url[2:3] != '/' and url[2:12].lower() != 'localhost/': - raise ValueError("file:// scheme is supported only on localhost") - else: - return self.open_local_file(url) - - def open_local_file(self, url): - """Use local file.""" - import future.backports.email.utils as email_utils - import mimetypes - host, file = splithost(url) - localname = url2pathname(file) - try: - stats = os.stat(localname) - except OSError as e: - raise URLError(e.strerror, e.filename) - size = stats.st_size - modified = email_utils.formatdate(stats.st_mtime, usegmt=True) - mtype = mimetypes.guess_type(url)[0] - headers = email.message_from_string( - 'Content-Type: %s\nContent-Length: %d\nLast-modified: %s\n' % - (mtype or 'text/plain', size, modified)) - if not host: - urlfile = file - if file[:1] == '/': - urlfile = 'file://' + file - return addinfourl(open(localname, 'rb'), headers, urlfile) - host, port = splitport(host) - if (not port - and socket.gethostbyname(host) in ((localhost(),) + thishost())): - urlfile = file - if file[:1] == '/': - urlfile = 'file://' + file - elif file[:2] == './': - raise ValueError("local file url may start with / or file:. Unknown url of type: %s" % url) - return addinfourl(open(localname, 'rb'), headers, urlfile) - raise URLError('local file error: not on local host') - - def open_ftp(self, url): - """Use FTP protocol.""" - if not isinstance(url, str): - raise URLError('ftp error: proxy support for ftp protocol currently not implemented') - import mimetypes - host, path = splithost(url) - if not host: raise URLError('ftp error: no host given') - host, port = splitport(host) - user, host = splituser(host) - if user: user, passwd = splitpasswd(user) - else: passwd = None - host = unquote(host) - user = unquote(user or '') - passwd = unquote(passwd or '') - host = socket.gethostbyname(host) - if not port: - import ftplib - port = ftplib.FTP_PORT - else: - port = int(port) - path, attrs = splitattr(path) - path = unquote(path) - dirs = path.split('/') - dirs, file = dirs[:-1], dirs[-1] - if dirs and not dirs[0]: dirs = dirs[1:] - if dirs and not dirs[0]: dirs[0] = '/' - key = user, host, port, '/'.join(dirs) - # XXX thread unsafe! - if len(self.ftpcache) > MAXFTPCACHE: - # Prune the cache, rather arbitrarily - for k in self.ftpcache.keys(): - if k != key: - v = self.ftpcache[k] - del self.ftpcache[k] - v.close() - try: - if key not in self.ftpcache: - self.ftpcache[key] = \ - ftpwrapper(user, passwd, host, port, dirs) - if not file: type = 'D' - else: type = 'I' - for attr in attrs: - attr, value = splitvalue(attr) - if attr.lower() == 'type' and \ - value in ('a', 'A', 'i', 'I', 'd', 'D'): - type = value.upper() - (fp, retrlen) = self.ftpcache[key].retrfile(file, type) - mtype = mimetypes.guess_type("ftp:" + url)[0] - headers = "" - if mtype: - headers += "Content-Type: %s\n" % mtype - if retrlen is not None and retrlen >= 0: - headers += "Content-Length: %d\n" % retrlen - headers = email.message_from_string(headers) - return addinfourl(fp, headers, "ftp:" + url) - except ftperrors() as exp: - raise_with_traceback(URLError('ftp error %r' % exp)) - - def open_data(self, url, data=None): - """Use "data" URL.""" - if not isinstance(url, str): - raise URLError('data error: proxy support for data protocol currently not implemented') - # ignore POSTed data - # - # syntax of data URLs: - # dataurl := "data:" [ mediatype ] [ ";base64" ] "," data - # mediatype := [ type "/" subtype ] *( ";" parameter ) - # data := *urlchar - # parameter := attribute "=" value - try: - [type, data] = url.split(',', 1) - except ValueError: - raise IOError('data error', 'bad data URL') - if not type: - type = 'text/plain;charset=US-ASCII' - semi = type.rfind(';') - if semi >= 0 and '=' not in type[semi:]: - encoding = type[semi+1:] - type = type[:semi] - else: - encoding = '' - msg = [] - msg.append('Date: %s'%time.strftime('%a, %d %b %Y %H:%M:%S GMT', - time.gmtime(time.time()))) - msg.append('Content-type: %s' % type) - if encoding == 'base64': - # XXX is this encoding/decoding ok? - data = base64.decodebytes(data.encode('ascii')).decode('latin-1') - else: - data = unquote(data) - msg.append('Content-Length: %d' % len(data)) - msg.append('') - msg.append(data) - msg = '\n'.join(msg) - headers = email.message_from_string(msg) - f = io.StringIO(msg) - #f.fileno = None # needed for addinfourl - return addinfourl(f, headers, url) - - -class FancyURLopener(URLopener): - """Derived class with handlers for errors we can handle (perhaps).""" - - def __init__(self, *args, **kwargs): - URLopener.__init__(self, *args, **kwargs) - self.auth_cache = {} - self.tries = 0 - self.maxtries = 10 - - def http_error_default(self, url, fp, errcode, errmsg, headers): - """Default error handling -- don't raise an exception.""" - return addinfourl(fp, headers, "http:" + url, errcode) - - def http_error_302(self, url, fp, errcode, errmsg, headers, data=None): - """Error 302 -- relocated (temporarily).""" - self.tries += 1 - if self.maxtries and self.tries >= self.maxtries: - if hasattr(self, "http_error_500"): - meth = self.http_error_500 - else: - meth = self.http_error_default - self.tries = 0 - return meth(url, fp, 500, - "Internal Server Error: Redirect Recursion", headers) - result = self.redirect_internal(url, fp, errcode, errmsg, headers, - data) - self.tries = 0 - return result - - def redirect_internal(self, url, fp, errcode, errmsg, headers, data): - if 'location' in headers: - newurl = headers['location'] - elif 'uri' in headers: - newurl = headers['uri'] - else: - return - fp.close() - - # In case the server sent a relative URL, join with original: - newurl = urljoin(self.type + ":" + url, newurl) - - urlparts = urlparse(newurl) - - # For security reasons, we don't allow redirection to anything other - # than http, https and ftp. - - # We are using newer HTTPError with older redirect_internal method - # This older method will get deprecated in 3.3 - - if urlparts.scheme not in ('http', 'https', 'ftp', ''): - raise HTTPError(newurl, errcode, - errmsg + - " Redirection to url '%s' is not allowed." % newurl, - headers, fp) - - return self.open(newurl) - - def http_error_301(self, url, fp, errcode, errmsg, headers, data=None): - """Error 301 -- also relocated (permanently).""" - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - - def http_error_303(self, url, fp, errcode, errmsg, headers, data=None): - """Error 303 -- also relocated (essentially identical to 302).""" - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - - def http_error_307(self, url, fp, errcode, errmsg, headers, data=None): - """Error 307 -- relocated, but turn POST into error.""" - if data is None: - return self.http_error_302(url, fp, errcode, errmsg, headers, data) - else: - return self.http_error_default(url, fp, errcode, errmsg, headers) - - def http_error_401(self, url, fp, errcode, errmsg, headers, data=None, - retry=False): - """Error 401 -- authentication required. - This function supports Basic authentication only.""" - if 'www-authenticate' not in headers: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - stuff = headers['www-authenticate'] - match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff) - if not match: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - scheme, realm = match.groups() - if scheme.lower() != 'basic': - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - if not retry: - URLopener.http_error_default(self, url, fp, errcode, errmsg, - headers) - name = 'retry_' + self.type + '_basic_auth' - if data is None: - return getattr(self,name)(url, realm) - else: - return getattr(self,name)(url, realm, data) - - def http_error_407(self, url, fp, errcode, errmsg, headers, data=None, - retry=False): - """Error 407 -- proxy authentication required. - This function supports Basic authentication only.""" - if 'proxy-authenticate' not in headers: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - stuff = headers['proxy-authenticate'] - match = re.match('[ \t]*([^ \t]+)[ \t]+realm="([^"]*)"', stuff) - if not match: - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - scheme, realm = match.groups() - if scheme.lower() != 'basic': - URLopener.http_error_default(self, url, fp, - errcode, errmsg, headers) - if not retry: - URLopener.http_error_default(self, url, fp, errcode, errmsg, - headers) - name = 'retry_proxy_' + self.type + '_basic_auth' - if data is None: - return getattr(self,name)(url, realm) - else: - return getattr(self,name)(url, realm, data) - - def retry_proxy_http_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - newurl = 'http://' + host + selector - proxy = self.proxies['http'] - urltype, proxyhost = splittype(proxy) - proxyhost, proxyselector = splithost(proxyhost) - i = proxyhost.find('@') + 1 - proxyhost = proxyhost[i:] - user, passwd = self.get_user_passwd(proxyhost, realm, i) - if not (user or passwd): return None - proxyhost = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), proxyhost) - self.proxies['http'] = 'http://' + proxyhost + proxyselector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_proxy_https_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - newurl = 'https://' + host + selector - proxy = self.proxies['https'] - urltype, proxyhost = splittype(proxy) - proxyhost, proxyselector = splithost(proxyhost) - i = proxyhost.find('@') + 1 - proxyhost = proxyhost[i:] - user, passwd = self.get_user_passwd(proxyhost, realm, i) - if not (user or passwd): return None - proxyhost = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), proxyhost) - self.proxies['https'] = 'https://' + proxyhost + proxyselector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_http_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - i = host.find('@') + 1 - host = host[i:] - user, passwd = self.get_user_passwd(host, realm, i) - if not (user or passwd): return None - host = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), host) - newurl = 'http://' + host + selector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def retry_https_basic_auth(self, url, realm, data=None): - host, selector = splithost(url) - i = host.find('@') + 1 - host = host[i:] - user, passwd = self.get_user_passwd(host, realm, i) - if not (user or passwd): return None - host = "%s:%s@%s" % (quote(user, safe=''), - quote(passwd, safe=''), host) - newurl = 'https://' + host + selector - if data is None: - return self.open(newurl) - else: - return self.open(newurl, data) - - def get_user_passwd(self, host, realm, clear_cache=0): - key = realm + '@' + host.lower() - if key in self.auth_cache: - if clear_cache: - del self.auth_cache[key] - else: - return self.auth_cache[key] - user, passwd = self.prompt_user_passwd(host, realm) - if user or passwd: self.auth_cache[key] = (user, passwd) - return user, passwd - - def prompt_user_passwd(self, host, realm): - """Override this in a GUI environment!""" - import getpass - try: - user = input("Enter username for %s at %s: " % (realm, host)) - passwd = getpass.getpass("Enter password for %s in %s at %s: " % - (user, realm, host)) - return user, passwd - except KeyboardInterrupt: - print() - return None, None - - -# Utility functions - -_localhost = None -def localhost(): - """Return the IP address of the magic hostname 'localhost'.""" - global _localhost - if _localhost is None: - _localhost = socket.gethostbyname('localhost') - return _localhost - -_thishost = None -def thishost(): - """Return the IP addresses of the current host.""" - global _thishost - if _thishost is None: - try: - _thishost = tuple(socket.gethostbyname_ex(socket.gethostname())[2]) - except socket.gaierror: - _thishost = tuple(socket.gethostbyname_ex('localhost')[2]) - return _thishost - -_ftperrors = None -def ftperrors(): - """Return the set of errors raised by the FTP class.""" - global _ftperrors - if _ftperrors is None: - import ftplib - _ftperrors = ftplib.all_errors - return _ftperrors - -_noheaders = None -def noheaders(): - """Return an empty email Message object.""" - global _noheaders - if _noheaders is None: - _noheaders = email.message_from_string("") - return _noheaders - - -# Utility classes - -class ftpwrapper(object): - """Class used by open_ftp() for cache of open FTP connections.""" - - def __init__(self, user, passwd, host, port, dirs, timeout=None, - persistent=True): - self.user = user - self.passwd = passwd - self.host = host - self.port = port - self.dirs = dirs - self.timeout = timeout - self.refcount = 0 - self.keepalive = persistent - self.init() - - def init(self): - import ftplib - self.busy = 0 - self.ftp = ftplib.FTP() - self.ftp.connect(self.host, self.port, self.timeout) - self.ftp.login(self.user, self.passwd) - _target = '/'.join(self.dirs) - self.ftp.cwd(_target) - - def retrfile(self, file, type): - import ftplib - self.endtransfer() - if type in ('d', 'D'): cmd = 'TYPE A'; isdir = 1 - else: cmd = 'TYPE ' + type; isdir = 0 - try: - self.ftp.voidcmd(cmd) - except ftplib.all_errors: - self.init() - self.ftp.voidcmd(cmd) - conn = None - if file and not isdir: - # Try to retrieve as a file - try: - cmd = 'RETR ' + file - conn, retrlen = self.ftp.ntransfercmd(cmd) - except ftplib.error_perm as reason: - if str(reason)[:3] != '550': - raise_with_traceback(URLError('ftp error: %r' % reason)) - if not conn: - # Set transfer mode to ASCII! - self.ftp.voidcmd('TYPE A') - # Try a directory listing. Verify that directory exists. - if file: - pwd = self.ftp.pwd() - try: - try: - self.ftp.cwd(file) - except ftplib.error_perm as reason: - ### Was: - # raise URLError('ftp error: %r' % reason) from reason - exc = URLError('ftp error: %r' % reason) - exc.__cause__ = reason - raise exc - finally: - self.ftp.cwd(pwd) - cmd = 'LIST ' + file - else: - cmd = 'LIST' - conn, retrlen = self.ftp.ntransfercmd(cmd) - self.busy = 1 - - ftpobj = addclosehook(conn.makefile('rb'), self.file_close) - self.refcount += 1 - conn.close() - # Pass back both a suitably decorated object and a retrieval length - return (ftpobj, retrlen) - - def endtransfer(self): - self.busy = 0 - - def close(self): - self.keepalive = False - if self.refcount <= 0: - self.real_close() - - def file_close(self): - self.endtransfer() - self.refcount -= 1 - if self.refcount <= 0 and not self.keepalive: - self.real_close() - - def real_close(self): - self.endtransfer() - try: - self.ftp.close() - except ftperrors(): - pass - -# Proxy handling -def getproxies_environment(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Scan the environment for variables named _proxy; - this seems to be the standard convention. If you need a - different way, you can pass a proxies dictionary to the - [Fancy]URLopener constructor. - - """ - proxies = {} - for name, value in os.environ.items(): - name = name.lower() - if value and name[-6:] == '_proxy': - proxies[name[:-6]] = value - return proxies - -def proxy_bypass_environment(host): - """Test if proxies should not be used for a particular host. - - Checks the environment for a variable named no_proxy, which should - be a list of DNS suffixes separated by commas, or '*' for all hosts. - """ - no_proxy = os.environ.get('no_proxy', '') or os.environ.get('NO_PROXY', '') - # '*' is special case for always bypass - if no_proxy == '*': - return 1 - # strip port off host - hostonly, port = splitport(host) - # check if the host ends with any of the DNS suffixes - no_proxy_list = [proxy.strip() for proxy in no_proxy.split(',')] - for name in no_proxy_list: - if name and (hostonly.endswith(name) or host.endswith(name)): - return 1 - # otherwise, don't bypass - return 0 - - -# This code tests an OSX specific data structure but is testable on all -# platforms -def _proxy_bypass_macosx_sysconf(host, proxy_settings): - """ - Return True iff this host shouldn't be accessed using a proxy - - This function uses the MacOSX framework SystemConfiguration - to fetch the proxy information. - - proxy_settings come from _scproxy._get_proxy_settings or get mocked ie: - { 'exclude_simple': bool, - 'exceptions': ['foo.bar', '*.bar.com', '127.0.0.1', '10.1', '10.0/16'] - } - """ - from fnmatch import fnmatch - - hostonly, port = splitport(host) - - def ip2num(ipAddr): - parts = ipAddr.split('.') - parts = list(map(int, parts)) - if len(parts) != 4: - parts = (parts + [0, 0, 0, 0])[:4] - return (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8) | parts[3] - - # Check for simple host names: - if '.' not in host: - if proxy_settings['exclude_simple']: - return True - - hostIP = None - - for value in proxy_settings.get('exceptions', ()): - # Items in the list are strings like these: *.local, 169.254/16 - if not value: continue - - m = re.match(r"(\d+(?:\.\d+)*)(/\d+)?", value) - if m is not None: - if hostIP is None: - try: - hostIP = socket.gethostbyname(hostonly) - hostIP = ip2num(hostIP) - except socket.error: - continue - - base = ip2num(m.group(1)) - mask = m.group(2) - if mask is None: - mask = 8 * (m.group(1).count('.') + 1) - else: - mask = int(mask[1:]) - mask = 32 - mask - - if (hostIP >> mask) == (base >> mask): - return True - - elif fnmatch(host, value): - return True - - return False - - -if sys.platform == 'darwin': - from _scproxy import _get_proxy_settings, _get_proxies - - def proxy_bypass_macosx_sysconf(host): - proxy_settings = _get_proxy_settings() - return _proxy_bypass_macosx_sysconf(host, proxy_settings) - - def getproxies_macosx_sysconf(): - """Return a dictionary of scheme -> proxy server URL mappings. - - This function uses the MacOSX framework SystemConfiguration - to fetch the proxy information. - """ - return _get_proxies() - - - - def proxy_bypass(host): - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_macosx_sysconf(host) - - def getproxies(): - return getproxies_environment() or getproxies_macosx_sysconf() - - -elif os.name == 'nt': - def getproxies_registry(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Win32 uses the registry to store proxies. - - """ - proxies = {} - try: - import winreg - except ImportError: - # Std module, so should be around - but you never know! - return proxies - try: - internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') - proxyEnable = winreg.QueryValueEx(internetSettings, - 'ProxyEnable')[0] - if proxyEnable: - # Returned as Unicode but problems if not converted to ASCII - proxyServer = str(winreg.QueryValueEx(internetSettings, - 'ProxyServer')[0]) - if '=' in proxyServer: - # Per-protocol settings - for p in proxyServer.split(';'): - protocol, address = p.split('=', 1) - # See if address has a type:// prefix - if not re.match('^([^/:]+)://', address): - address = '%s://%s' % (protocol, address) - proxies[protocol] = address - else: - # Use one setting for all protocols - if proxyServer[:5] == 'http:': - proxies['http'] = proxyServer - else: - proxies['http'] = 'http://%s' % proxyServer - proxies['https'] = 'https://%s' % proxyServer - proxies['ftp'] = 'ftp://%s' % proxyServer - internetSettings.Close() - except (WindowsError, ValueError, TypeError): - # Either registry key not found etc, or the value in an - # unexpected format. - # proxies already set up to be empty so nothing to do - pass - return proxies - - def getproxies(): - """Return a dictionary of scheme -> proxy server URL mappings. - - Returns settings gathered from the environment, if specified, - or the registry. - - """ - return getproxies_environment() or getproxies_registry() - - def proxy_bypass_registry(host): - try: - import winreg - except ImportError: - # Std modules, so should be around - but you never know! - return 0 - try: - internetSettings = winreg.OpenKey(winreg.HKEY_CURRENT_USER, - r'Software\Microsoft\Windows\CurrentVersion\Internet Settings') - proxyEnable = winreg.QueryValueEx(internetSettings, - 'ProxyEnable')[0] - proxyOverride = str(winreg.QueryValueEx(internetSettings, - 'ProxyOverride')[0]) - # ^^^^ Returned as Unicode but problems if not converted to ASCII - except WindowsError: - return 0 - if not proxyEnable or not proxyOverride: - return 0 - # try to make a host list from name and IP address. - rawHost, port = splitport(host) - host = [rawHost] - try: - addr = socket.gethostbyname(rawHost) - if addr != rawHost: - host.append(addr) - except socket.error: - pass - try: - fqdn = socket.getfqdn(rawHost) - if fqdn != rawHost: - host.append(fqdn) - except socket.error: - pass - # make a check value list from the registry entry: replace the - # '' string by the localhost entry and the corresponding - # canonical entry. - proxyOverride = proxyOverride.split(';') - # now check if we match one of the registry values. - for test in proxyOverride: - if test == '': - if '.' not in rawHost: - return 1 - test = test.replace(".", r"\.") # mask dots - test = test.replace("*", r".*") # change glob sequence - test = test.replace("?", r".") # change glob char - for val in host: - if re.match(test, val, re.I): - return 1 - return 0 - - def proxy_bypass(host): - """Return a dictionary of scheme -> proxy server URL mappings. - - Returns settings gathered from the environment, if specified, - or the registry. - - """ - if getproxies_environment(): - return proxy_bypass_environment(host) - else: - return proxy_bypass_registry(host) - -else: - # By default use environment variables - getproxies = getproxies_environment - proxy_bypass = proxy_bypass_environment diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/response.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/response.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/response.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/response.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -"""Response classes used by urllib. - -The base class, addbase, defines a minimal file-like interface, -including read() and readline(). The typical response object is an -addinfourl instance, which defines an info() method that returns -headers and a geturl() method that returns the url. -""" -from __future__ import absolute_import, division, unicode_literals -from future.builtins import object - -class addbase(object): - """Base class for addinfo and addclosehook.""" - - # XXX Add a method to expose the timeout on the underlying socket? - - def __init__(self, fp): - # TODO(jhylton): Is there a better way to delegate using io? - self.fp = fp - self.read = self.fp.read - self.readline = self.fp.readline - # TODO(jhylton): Make sure an object with readlines() is also iterable - if hasattr(self.fp, "readlines"): - self.readlines = self.fp.readlines - if hasattr(self.fp, "fileno"): - self.fileno = self.fp.fileno - else: - self.fileno = lambda: None - - def __iter__(self): - # Assigning `__iter__` to the instance doesn't work as intended - # because the iter builtin does something like `cls.__iter__(obj)` - # and thus fails to find the _bound_ method `obj.__iter__`. - # Returning just `self.fp` works for built-in file objects but - # might not work for general file-like objects. - return iter(self.fp) - - def __repr__(self): - return '<%s at %r whose fp = %r>' % (self.__class__.__name__, - id(self), self.fp) - - def close(self): - if self.fp: - self.fp.close() - self.fp = None - self.read = None - self.readline = None - self.readlines = None - self.fileno = None - self.__iter__ = None - self.__next__ = None - - def __enter__(self): - if self.fp is None: - raise ValueError("I/O operation on closed file") - return self - - def __exit__(self, type, value, traceback): - self.close() - -class addclosehook(addbase): - """Class to add a close hook to an open file.""" - - def __init__(self, fp, closehook, *hookargs): - addbase.__init__(self, fp) - self.closehook = closehook - self.hookargs = hookargs - - def close(self): - if self.closehook: - self.closehook(*self.hookargs) - self.closehook = None - self.hookargs = None - addbase.close(self) - -class addinfo(addbase): - """class to add an info() method to an open file.""" - - def __init__(self, fp, headers): - addbase.__init__(self, fp) - self.headers = headers - - def info(self): - return self.headers - -class addinfourl(addbase): - """class to add info() and geturl() methods to an open file.""" - - def __init__(self, fp, headers, url, code=None): - addbase.__init__(self, fp) - self.headers = headers - self.url = url - self.code = code - - def info(self): - return self.headers - - def getcode(self): - return self.code - - def geturl(self): - return self.url - -del absolute_import, division, unicode_literals, object diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/robotparser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/robotparser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/urllib/robotparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/urllib/robotparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,211 +0,0 @@ -from __future__ import absolute_import, division, unicode_literals -from future.builtins import str -""" robotparser.py - - Copyright (C) 2000 Bastian Kleineidam - - You can choose between two licenses when using this package: - 1) GNU GPLv2 - 2) PSF license for Python 2.2 - - The robots.txt Exclusion Protocol is implemented as specified in - http://info.webcrawler.com/mak/projects/robots/norobots-rfc.html -""" - -# Was: import urllib.parse, urllib.request -from future.backports import urllib -from future.backports.urllib import parse as _parse, request as _request -urllib.parse = _parse -urllib.request = _request - - -__all__ = ["RobotFileParser"] - -class RobotFileParser(object): - """ This class provides a set of methods to read, parse and answer - questions about a single robots.txt file. - - """ - - def __init__(self, url=''): - self.entries = [] - self.default_entry = None - self.disallow_all = False - self.allow_all = False - self.set_url(url) - self.last_checked = 0 - - def mtime(self): - """Returns the time the robots.txt file was last fetched. - - This is useful for long-running web spiders that need to - check for new robots.txt files periodically. - - """ - return self.last_checked - - def modified(self): - """Sets the time the robots.txt file was last fetched to the - current time. - - """ - import time - self.last_checked = time.time() - - def set_url(self, url): - """Sets the URL referring to a robots.txt file.""" - self.url = url - self.host, self.path = urllib.parse.urlparse(url)[1:3] - - def read(self): - """Reads the robots.txt URL and feeds it to the parser.""" - try: - f = urllib.request.urlopen(self.url) - except urllib.error.HTTPError as err: - if err.code in (401, 403): - self.disallow_all = True - elif err.code >= 400: - self.allow_all = True - else: - raw = f.read() - self.parse(raw.decode("utf-8").splitlines()) - - def _add_entry(self, entry): - if "*" in entry.useragents: - # the default entry is considered last - if self.default_entry is None: - # the first default entry wins - self.default_entry = entry - else: - self.entries.append(entry) - - def parse(self, lines): - """Parse the input lines from a robots.txt file. - - We allow that a user-agent: line is not preceded by - one or more blank lines. - """ - # states: - # 0: start state - # 1: saw user-agent line - # 2: saw an allow or disallow line - state = 0 - entry = Entry() - - for line in lines: - if not line: - if state == 1: - entry = Entry() - state = 0 - elif state == 2: - self._add_entry(entry) - entry = Entry() - state = 0 - # remove optional comment and strip line - i = line.find('#') - if i >= 0: - line = line[:i] - line = line.strip() - if not line: - continue - line = line.split(':', 1) - if len(line) == 2: - line[0] = line[0].strip().lower() - line[1] = urllib.parse.unquote(line[1].strip()) - if line[0] == "user-agent": - if state == 2: - self._add_entry(entry) - entry = Entry() - entry.useragents.append(line[1]) - state = 1 - elif line[0] == "disallow": - if state != 0: - entry.rulelines.append(RuleLine(line[1], False)) - state = 2 - elif line[0] == "allow": - if state != 0: - entry.rulelines.append(RuleLine(line[1], True)) - state = 2 - if state == 2: - self._add_entry(entry) - - - def can_fetch(self, useragent, url): - """using the parsed robots.txt decide if useragent can fetch url""" - if self.disallow_all: - return False - if self.allow_all: - return True - # search for given user agent matches - # the first match counts - parsed_url = urllib.parse.urlparse(urllib.parse.unquote(url)) - url = urllib.parse.urlunparse(('','',parsed_url.path, - parsed_url.params,parsed_url.query, parsed_url.fragment)) - url = urllib.parse.quote(url) - if not url: - url = "/" - for entry in self.entries: - if entry.applies_to(useragent): - return entry.allowance(url) - # try the default entry last - if self.default_entry: - return self.default_entry.allowance(url) - # agent not found ==> access granted - return True - - def __str__(self): - return ''.join([str(entry) + "\n" for entry in self.entries]) - - -class RuleLine(object): - """A rule line is a single "Allow:" (allowance==True) or "Disallow:" - (allowance==False) followed by a path.""" - def __init__(self, path, allowance): - if path == '' and not allowance: - # an empty value means allow all - allowance = True - self.path = urllib.parse.quote(path) - self.allowance = allowance - - def applies_to(self, filename): - return self.path == "*" or filename.startswith(self.path) - - def __str__(self): - return (self.allowance and "Allow" or "Disallow") + ": " + self.path - - -class Entry(object): - """An entry has one or more user-agents and zero or more rulelines""" - def __init__(self): - self.useragents = [] - self.rulelines = [] - - def __str__(self): - ret = [] - for agent in self.useragents: - ret.extend(["User-agent: ", agent, "\n"]) - for line in self.rulelines: - ret.extend([str(line), "\n"]) - return ''.join(ret) - - def applies_to(self, useragent): - """check if this entry applies to the specified agent""" - # split the name token and make it lower case - useragent = useragent.split("/")[0].lower() - for agent in self.useragents: - if agent == '*': - # we have the catch-all agent - return True - agent = agent.lower() - if agent in useragent: - return True - return False - - def allowance(self, filename): - """Preconditions: - - our agent applies to this entry - - filename is URL decoded""" - for line in self.rulelines: - if line.applies_to(filename): - return line.allowance - return True diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/xmlrpc/client.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/xmlrpc/client.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/xmlrpc/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/xmlrpc/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,1496 +0,0 @@ -# -# XML-RPC CLIENT LIBRARY -# $Id$ -# -# an XML-RPC client interface for Python. -# -# the marshalling and response parser code can also be used to -# implement XML-RPC servers. -# -# Notes: -# this version is designed to work with Python 2.1 or newer. -# -# History: -# 1999-01-14 fl Created -# 1999-01-15 fl Changed dateTime to use localtime -# 1999-01-16 fl Added Binary/base64 element, default to RPC2 service -# 1999-01-19 fl Fixed array data element (from Skip Montanaro) -# 1999-01-21 fl Fixed dateTime constructor, etc. -# 1999-02-02 fl Added fault handling, handle empty sequences, etc. -# 1999-02-10 fl Fixed problem with empty responses (from Skip Montanaro) -# 1999-06-20 fl Speed improvements, pluggable parsers/transports (0.9.8) -# 2000-11-28 fl Changed boolean to check the truth value of its argument -# 2001-02-24 fl Added encoding/Unicode/SafeTransport patches -# 2001-02-26 fl Added compare support to wrappers (0.9.9/1.0b1) -# 2001-03-28 fl Make sure response tuple is a singleton -# 2001-03-29 fl Don't require empty params element (from Nicholas Riley) -# 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2) -# 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod) -# 2001-09-03 fl Allow Transport subclass to override getparser -# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup) -# 2001-10-01 fl Remove containers from memo cache when done with them -# 2001-10-01 fl Use faster escape method (80% dumps speedup) -# 2001-10-02 fl More dumps microtuning -# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum) -# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow -# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems) -# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix) -# 2002-03-17 fl Avoid buffered read when possible (from James Rucker) -# 2002-04-07 fl Added pythondoc comments -# 2002-04-16 fl Added __str__ methods to datetime/binary wrappers -# 2002-05-15 fl Added error constants (from Andrew Kuchling) -# 2002-06-27 fl Merged with Python CVS version -# 2002-10-22 fl Added basic authentication (based on code from Phillip Eby) -# 2003-01-22 sm Add support for the bool type -# 2003-02-27 gvr Remove apply calls -# 2003-04-24 sm Use cStringIO if available -# 2003-04-25 ak Add support for nil -# 2003-06-15 gn Add support for time.struct_time -# 2003-07-12 gp Correct marshalling of Faults -# 2003-10-31 mvl Add multicall support -# 2004-08-20 mvl Bump minimum supported Python version to 2.1 -# -# Copyright (c) 1999-2002 by Secret Labs AB. -# Copyright (c) 1999-2002 by Fredrik Lundh. -# -# info@pythonware.com -# http://www.pythonware.com -# -# -------------------------------------------------------------------- -# The XML-RPC client interface is -# -# Copyright (c) 1999-2002 by Secret Labs AB -# Copyright (c) 1999-2002 by Fredrik Lundh -# -# By obtaining, using, and/or copying this software and/or its -# associated documentation, you agree that you have read, understood, -# and will comply with the following terms and conditions: -# -# Permission to use, copy, modify, and distribute this software and -# its associated documentation for any purpose and without fee is -# hereby granted, provided that the above copyright notice appears in -# all copies, and that both that copyright notice and this permission -# notice appear in supporting documentation, and that the name of -# Secret Labs AB or the author not be used in advertising or publicity -# pertaining to distribution of the software without specific, written -# prior permission. -# -# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD -# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- -# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR -# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY -# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, -# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS -# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE -# OF THIS SOFTWARE. -# -------------------------------------------------------------------- - -""" -Ported using Python-Future from the Python 3.3 standard library. - -An XML-RPC client interface for Python. - -The marshalling and response parser code can also be used to -implement XML-RPC servers. - -Exported exceptions: - - Error Base class for client errors - ProtocolError Indicates an HTTP protocol error - ResponseError Indicates a broken response package - Fault Indicates an XML-RPC fault package - -Exported classes: - - ServerProxy Represents a logical connection to an XML-RPC server - - MultiCall Executor of boxcared xmlrpc requests - DateTime dateTime wrapper for an ISO 8601 string or time tuple or - localtime integer value to generate a "dateTime.iso8601" - XML-RPC value - Binary binary data wrapper - - Marshaller Generate an XML-RPC params chunk from a Python data structure - Unmarshaller Unmarshal an XML-RPC response from incoming XML event message - Transport Handles an HTTP transaction to an XML-RPC server - SafeTransport Handles an HTTPS transaction to an XML-RPC server - -Exported constants: - - (none) - -Exported functions: - - getparser Create instance of the fastest available parser & attach - to an unmarshalling object - dumps Convert an argument tuple or a Fault instance to an XML-RPC - request (or response, if the methodresponse option is used). - loads Convert an XML-RPC packet to unmarshalled data plus a method - name (None if not present). -""" - -from __future__ import (absolute_import, division, print_function, - unicode_literals) -from future.builtins import bytes, dict, int, range, str - -import base64 -# Py2.7 compatibility hack -base64.encodebytes = base64.encodestring -base64.decodebytes = base64.decodestring -import sys -import time -from datetime import datetime -from future.backports.http import client as http_client -from future.backports.urllib import parse as urllib_parse -from future.utils import ensure_new_type -from xml.parsers import expat -import socket -import errno -from io import BytesIO -try: - import gzip -except ImportError: - gzip = None #python can be built without zlib/gzip support - -# -------------------------------------------------------------------- -# Internal stuff - -def escape(s): - s = s.replace("&", "&") - s = s.replace("<", "<") - return s.replace(">", ">",) - -# used in User-Agent header sent -__version__ = sys.version[:3] - -# xmlrpc integer limits -MAXINT = 2**31-1 -MININT = -2**31 - -# -------------------------------------------------------------------- -# Error constants (from Dan Libby's specification at -# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php) - -# Ranges of errors -PARSE_ERROR = -32700 -SERVER_ERROR = -32600 -APPLICATION_ERROR = -32500 -SYSTEM_ERROR = -32400 -TRANSPORT_ERROR = -32300 - -# Specific errors -NOT_WELLFORMED_ERROR = -32700 -UNSUPPORTED_ENCODING = -32701 -INVALID_ENCODING_CHAR = -32702 -INVALID_XMLRPC = -32600 -METHOD_NOT_FOUND = -32601 -INVALID_METHOD_PARAMS = -32602 -INTERNAL_ERROR = -32603 - -# -------------------------------------------------------------------- -# Exceptions - -## -# Base class for all kinds of client-side errors. - -class Error(Exception): - """Base class for client errors.""" - def __str__(self): - return repr(self) - -## -# Indicates an HTTP-level protocol error. This is raised by the HTTP -# transport layer, if the server returns an error code other than 200 -# (OK). -# -# @param url The target URL. -# @param errcode The HTTP error code. -# @param errmsg The HTTP error message. -# @param headers The HTTP header dictionary. - -class ProtocolError(Error): - """Indicates an HTTP protocol error.""" - def __init__(self, url, errcode, errmsg, headers): - Error.__init__(self) - self.url = url - self.errcode = errcode - self.errmsg = errmsg - self.headers = headers - def __repr__(self): - return ( - "" % - (self.url, self.errcode, self.errmsg) - ) - -## -# Indicates a broken XML-RPC response package. This exception is -# raised by the unmarshalling layer, if the XML-RPC response is -# malformed. - -class ResponseError(Error): - """Indicates a broken response package.""" - pass - -## -# Indicates an XML-RPC fault response package. This exception is -# raised by the unmarshalling layer, if the XML-RPC response contains -# a fault string. This exception can also be used as a class, to -# generate a fault XML-RPC message. -# -# @param faultCode The XML-RPC fault code. -# @param faultString The XML-RPC fault string. - -class Fault(Error): - """Indicates an XML-RPC fault package.""" - def __init__(self, faultCode, faultString, **extra): - Error.__init__(self) - self.faultCode = faultCode - self.faultString = faultString - def __repr__(self): - return "" % (ensure_new_type(self.faultCode), - ensure_new_type(self.faultString)) - -# -------------------------------------------------------------------- -# Special values - -## -# Backwards compatibility - -boolean = Boolean = bool - -## -# Wrapper for XML-RPC DateTime values. This converts a time value to -# the format used by XML-RPC. -#

-# The value can be given as a datetime object, as a string in the -# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by -# time.localtime()), or an integer value (as returned by time.time()). -# The wrapper uses time.localtime() to convert an integer to a time -# tuple. -# -# @param value The time, given as a datetime object, an ISO 8601 string, -# a time tuple, or an integer time value. - - -### For Python-Future: -def _iso8601_format(value): - return "%04d%02d%02dT%02d:%02d:%02d" % ( - value.year, value.month, value.day, - value.hour, value.minute, value.second) -### -# Issue #13305: different format codes across platforms -# _day0 = datetime(1, 1, 1) -# if _day0.strftime('%Y') == '0001': # Mac OS X -# def _iso8601_format(value): -# return value.strftime("%Y%m%dT%H:%M:%S") -# elif _day0.strftime('%4Y') == '0001': # Linux -# def _iso8601_format(value): -# return value.strftime("%4Y%m%dT%H:%M:%S") -# else: -# def _iso8601_format(value): -# return value.strftime("%Y%m%dT%H:%M:%S").zfill(17) -# del _day0 - - -def _strftime(value): - if isinstance(value, datetime): - return _iso8601_format(value) - - if not isinstance(value, (tuple, time.struct_time)): - if value == 0: - value = time.time() - value = time.localtime(value) - - return "%04d%02d%02dT%02d:%02d:%02d" % value[:6] - -class DateTime(object): - """DateTime wrapper for an ISO 8601 string or time tuple or - localtime integer value to generate 'dateTime.iso8601' XML-RPC - value. - """ - - def __init__(self, value=0): - if isinstance(value, str): - self.value = value - else: - self.value = _strftime(value) - - def make_comparable(self, other): - if isinstance(other, DateTime): - s = self.value - o = other.value - elif isinstance(other, datetime): - s = self.value - o = _iso8601_format(other) - elif isinstance(other, str): - s = self.value - o = other - elif hasattr(other, "timetuple"): - s = self.timetuple() - o = other.timetuple() - else: - otype = (hasattr(other, "__class__") - and other.__class__.__name__ - or type(other)) - raise TypeError("Can't compare %s and %s" % - (self.__class__.__name__, otype)) - return s, o - - def __lt__(self, other): - s, o = self.make_comparable(other) - return s < o - - def __le__(self, other): - s, o = self.make_comparable(other) - return s <= o - - def __gt__(self, other): - s, o = self.make_comparable(other) - return s > o - - def __ge__(self, other): - s, o = self.make_comparable(other) - return s >= o - - def __eq__(self, other): - s, o = self.make_comparable(other) - return s == o - - def __ne__(self, other): - s, o = self.make_comparable(other) - return s != o - - def timetuple(self): - return time.strptime(self.value, "%Y%m%dT%H:%M:%S") - - ## - # Get date/time value. - # - # @return Date/time value, as an ISO 8601 string. - - def __str__(self): - return self.value - - def __repr__(self): - return "" % (ensure_new_type(self.value), id(self)) - - def decode(self, data): - self.value = str(data).strip() - - def encode(self, out): - out.write("") - out.write(self.value) - out.write("\n") - -def _datetime(data): - # decode xml element contents into a DateTime structure. - value = DateTime() - value.decode(data) - return value - -def _datetime_type(data): - return datetime.strptime(data, "%Y%m%dT%H:%M:%S") - -## -# Wrapper for binary data. This can be used to transport any kind -# of binary data over XML-RPC, using BASE64 encoding. -# -# @param data An 8-bit string containing arbitrary data. - -class Binary(object): - """Wrapper for binary data.""" - - def __init__(self, data=None): - if data is None: - data = b"" - else: - if not isinstance(data, (bytes, bytearray)): - raise TypeError("expected bytes or bytearray, not %s" % - data.__class__.__name__) - data = bytes(data) # Make a copy of the bytes! - self.data = data - - ## - # Get buffer contents. - # - # @return Buffer contents, as an 8-bit string. - - def __str__(self): - return str(self.data, "latin-1") # XXX encoding?! - - def __eq__(self, other): - if isinstance(other, Binary): - other = other.data - return self.data == other - - def __ne__(self, other): - if isinstance(other, Binary): - other = other.data - return self.data != other - - def decode(self, data): - self.data = base64.decodebytes(data) - - def encode(self, out): - out.write("\n") - encoded = base64.encodebytes(self.data) - out.write(encoded.decode('ascii')) - out.write("\n") - -def _binary(data): - # decode xml element contents into a Binary structure - value = Binary() - value.decode(data) - return value - -WRAPPERS = (DateTime, Binary) - -# -------------------------------------------------------------------- -# XML parsers - -class ExpatParser(object): - # fast expat parser for Python 2.0 and later. - def __init__(self, target): - self._parser = parser = expat.ParserCreate(None, None) - self._target = target - parser.StartElementHandler = target.start - parser.EndElementHandler = target.end - parser.CharacterDataHandler = target.data - encoding = None - target.xml(encoding, None) - - def feed(self, data): - self._parser.Parse(data, 0) - - def close(self): - self._parser.Parse("", 1) # end of data - del self._target, self._parser # get rid of circular references - -# -------------------------------------------------------------------- -# XML-RPC marshalling and unmarshalling code - -## -# XML-RPC marshaller. -# -# @param encoding Default encoding for 8-bit strings. The default -# value is None (interpreted as UTF-8). -# @see dumps - -class Marshaller(object): - """Generate an XML-RPC params chunk from a Python data structure. - - Create a Marshaller instance for each set of parameters, and use - the "dumps" method to convert your data (represented as a tuple) - to an XML-RPC params chunk. To write a fault response, pass a - Fault instance instead. You may prefer to use the "dumps" module - function for this purpose. - """ - - # by the way, if you don't understand what's going on in here, - # that's perfectly ok. - - def __init__(self, encoding=None, allow_none=False): - self.memo = {} - self.data = None - self.encoding = encoding - self.allow_none = allow_none - - dispatch = {} - - def dumps(self, values): - out = [] - write = out.append - dump = self.__dump - if isinstance(values, Fault): - # fault instance - write("\n") - dump({'faultCode': values.faultCode, - 'faultString': values.faultString}, - write) - write("\n") - else: - # parameter block - # FIXME: the xml-rpc specification allows us to leave out - # the entire block if there are no parameters. - # however, changing this may break older code (including - # old versions of xmlrpclib.py), so this is better left as - # is for now. See @XMLRPC3 for more information. /F - write("\n") - for v in values: - write("\n") - dump(v, write) - write("\n") - write("\n") - result = "".join(out) - return str(result) - - def __dump(self, value, write): - try: - f = self.dispatch[type(ensure_new_type(value))] - except KeyError: - # check if this object can be marshalled as a structure - if not hasattr(value, '__dict__'): - raise TypeError("cannot marshal %s objects" % type(value)) - # check if this class is a sub-class of a basic type, - # because we don't know how to marshal these types - # (e.g. a string sub-class) - for type_ in type(value).__mro__: - if type_ in self.dispatch.keys(): - raise TypeError("cannot marshal %s objects" % type(value)) - # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix - # for the p3yk merge, this should probably be fixed more neatly. - f = self.dispatch["_arbitrary_instance"] - f(self, value, write) - - def dump_nil (self, value, write): - if not self.allow_none: - raise TypeError("cannot marshal None unless allow_none is enabled") - write("") - dispatch[type(None)] = dump_nil - - def dump_bool(self, value, write): - write("") - write(value and "1" or "0") - write("\n") - dispatch[bool] = dump_bool - - def dump_long(self, value, write): - if value > MAXINT or value < MININT: - raise OverflowError("long int exceeds XML-RPC limits") - write("") - write(str(int(value))) - write("\n") - dispatch[int] = dump_long - - # backward compatible - dump_int = dump_long - - def dump_double(self, value, write): - write("") - write(repr(ensure_new_type(value))) - write("\n") - dispatch[float] = dump_double - - def dump_unicode(self, value, write, escape=escape): - write("") - write(escape(value)) - write("\n") - dispatch[str] = dump_unicode - - def dump_bytes(self, value, write): - write("\n") - encoded = base64.encodebytes(value) - write(encoded.decode('ascii')) - write("\n") - dispatch[bytes] = dump_bytes - dispatch[bytearray] = dump_bytes - - def dump_array(self, value, write): - i = id(value) - if i in self.memo: - raise TypeError("cannot marshal recursive sequences") - self.memo[i] = None - dump = self.__dump - write("\n") - for v in value: - dump(v, write) - write("\n") - del self.memo[i] - dispatch[tuple] = dump_array - dispatch[list] = dump_array - - def dump_struct(self, value, write, escape=escape): - i = id(value) - if i in self.memo: - raise TypeError("cannot marshal recursive dictionaries") - self.memo[i] = None - dump = self.__dump - write("\n") - for k, v in value.items(): - write("\n") - if not isinstance(k, str): - raise TypeError("dictionary key must be string") - write("%s\n" % escape(k)) - dump(v, write) - write("\n") - write("\n") - del self.memo[i] - dispatch[dict] = dump_struct - - def dump_datetime(self, value, write): - write("") - write(_strftime(value)) - write("\n") - dispatch[datetime] = dump_datetime - - def dump_instance(self, value, write): - # check for special wrappers - if value.__class__ in WRAPPERS: - self.write = write - value.encode(self) - del self.write - else: - # store instance attributes as a struct (really?) - self.dump_struct(value.__dict__, write) - dispatch[DateTime] = dump_instance - dispatch[Binary] = dump_instance - # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix - # for the p3yk merge, this should probably be fixed more neatly. - dispatch["_arbitrary_instance"] = dump_instance - -## -# XML-RPC unmarshaller. -# -# @see loads - -class Unmarshaller(object): - """Unmarshal an XML-RPC response, based on incoming XML event - messages (start, data, end). Call close() to get the resulting - data structure. - - Note that this reader is fairly tolerant, and gladly accepts bogus - XML-RPC data without complaining (but not bogus XML). - """ - - # and again, if you don't understand what's going on in here, - # that's perfectly ok. - - def __init__(self, use_datetime=False, use_builtin_types=False): - self._type = None - self._stack = [] - self._marks = [] - self._data = [] - self._methodname = None - self._encoding = "utf-8" - self.append = self._stack.append - self._use_datetime = use_builtin_types or use_datetime - self._use_bytes = use_builtin_types - - def close(self): - # return response tuple and target method - if self._type is None or self._marks: - raise ResponseError() - if self._type == "fault": - raise Fault(**self._stack[0]) - return tuple(self._stack) - - def getmethodname(self): - return self._methodname - - # - # event handlers - - def xml(self, encoding, standalone): - self._encoding = encoding - # FIXME: assert standalone == 1 ??? - - def start(self, tag, attrs): - # prepare to handle this element - if tag == "array" or tag == "struct": - self._marks.append(len(self._stack)) - self._data = [] - self._value = (tag == "value") - - def data(self, text): - self._data.append(text) - - def end(self, tag): - # call the appropriate end tag handler - try: - f = self.dispatch[tag] - except KeyError: - pass # unknown tag ? - else: - return f(self, "".join(self._data)) - - # - # accelerator support - - def end_dispatch(self, tag, data): - # dispatch data - try: - f = self.dispatch[tag] - except KeyError: - pass # unknown tag ? - else: - return f(self, data) - - # - # element decoders - - dispatch = {} - - def end_nil (self, data): - self.append(None) - self._value = 0 - dispatch["nil"] = end_nil - - def end_boolean(self, data): - if data == "0": - self.append(False) - elif data == "1": - self.append(True) - else: - raise TypeError("bad boolean value") - self._value = 0 - dispatch["boolean"] = end_boolean - - def end_int(self, data): - self.append(int(data)) - self._value = 0 - dispatch["i4"] = end_int - dispatch["i8"] = end_int - dispatch["int"] = end_int - - def end_double(self, data): - self.append(float(data)) - self._value = 0 - dispatch["double"] = end_double - - def end_string(self, data): - if self._encoding: - data = data.decode(self._encoding) - self.append(data) - self._value = 0 - dispatch["string"] = end_string - dispatch["name"] = end_string # struct keys are always strings - - def end_array(self, data): - mark = self._marks.pop() - # map arrays to Python lists - self._stack[mark:] = [self._stack[mark:]] - self._value = 0 - dispatch["array"] = end_array - - def end_struct(self, data): - mark = self._marks.pop() - # map structs to Python dictionaries - dict = {} - items = self._stack[mark:] - for i in range(0, len(items), 2): - dict[items[i]] = items[i+1] - self._stack[mark:] = [dict] - self._value = 0 - dispatch["struct"] = end_struct - - def end_base64(self, data): - value = Binary() - value.decode(data.encode("ascii")) - if self._use_bytes: - value = value.data - self.append(value) - self._value = 0 - dispatch["base64"] = end_base64 - - def end_dateTime(self, data): - value = DateTime() - value.decode(data) - if self._use_datetime: - value = _datetime_type(data) - self.append(value) - dispatch["dateTime.iso8601"] = end_dateTime - - def end_value(self, data): - # if we stumble upon a value element with no internal - # elements, treat it as a string element - if self._value: - self.end_string(data) - dispatch["value"] = end_value - - def end_params(self, data): - self._type = "params" - dispatch["params"] = end_params - - def end_fault(self, data): - self._type = "fault" - dispatch["fault"] = end_fault - - def end_methodName(self, data): - if self._encoding: - data = data.decode(self._encoding) - self._methodname = data - self._type = "methodName" # no params - dispatch["methodName"] = end_methodName - -## Multicall support -# - -class _MultiCallMethod(object): - # some lesser magic to store calls made to a MultiCall object - # for batch execution - def __init__(self, call_list, name): - self.__call_list = call_list - self.__name = name - def __getattr__(self, name): - return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name)) - def __call__(self, *args): - self.__call_list.append((self.__name, args)) - -class MultiCallIterator(object): - """Iterates over the results of a multicall. Exceptions are - raised in response to xmlrpc faults.""" - - def __init__(self, results): - self.results = results - - def __getitem__(self, i): - item = self.results[i] - if isinstance(type(item), dict): - raise Fault(item['faultCode'], item['faultString']) - elif type(item) == type([]): - return item[0] - else: - raise ValueError("unexpected type in multicall result") - -class MultiCall(object): - """server -> a object used to boxcar method calls - - server should be a ServerProxy object. - - Methods can be added to the MultiCall using normal - method call syntax e.g.: - - multicall = MultiCall(server_proxy) - multicall.add(2,3) - multicall.get_address("Guido") - - To execute the multicall, call the MultiCall object e.g.: - - add_result, address = multicall() - """ - - def __init__(self, server): - self.__server = server - self.__call_list = [] - - def __repr__(self): - return "" % id(self) - - __str__ = __repr__ - - def __getattr__(self, name): - return _MultiCallMethod(self.__call_list, name) - - def __call__(self): - marshalled_list = [] - for name, args in self.__call_list: - marshalled_list.append({'methodName' : name, 'params' : args}) - - return MultiCallIterator(self.__server.system.multicall(marshalled_list)) - -# -------------------------------------------------------------------- -# convenience functions - -FastMarshaller = FastParser = FastUnmarshaller = None - -## -# Create a parser object, and connect it to an unmarshalling instance. -# This function picks the fastest available XML parser. -# -# return A (parser, unmarshaller) tuple. - -def getparser(use_datetime=False, use_builtin_types=False): - """getparser() -> parser, unmarshaller - - Create an instance of the fastest available parser, and attach it - to an unmarshalling object. Return both objects. - """ - if FastParser and FastUnmarshaller: - if use_builtin_types: - mkdatetime = _datetime_type - mkbytes = base64.decodebytes - elif use_datetime: - mkdatetime = _datetime_type - mkbytes = _binary - else: - mkdatetime = _datetime - mkbytes = _binary - target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault) - parser = FastParser(target) - else: - target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types) - if FastParser: - parser = FastParser(target) - else: - parser = ExpatParser(target) - return parser, target - -## -# Convert a Python tuple or a Fault instance to an XML-RPC packet. -# -# @def dumps(params, **options) -# @param params A tuple or Fault instance. -# @keyparam methodname If given, create a methodCall request for -# this method name. -# @keyparam methodresponse If given, create a methodResponse packet. -# If used with a tuple, the tuple must be a singleton (that is, -# it must contain exactly one element). -# @keyparam encoding The packet encoding. -# @return A string containing marshalled data. - -def dumps(params, methodname=None, methodresponse=None, encoding=None, - allow_none=False): - """data [,options] -> marshalled data - - Convert an argument tuple or a Fault instance to an XML-RPC - request (or response, if the methodresponse option is used). - - In addition to the data object, the following options can be given - as keyword arguments: - - methodname: the method name for a methodCall packet - - methodresponse: true to create a methodResponse packet. - If this option is used with a tuple, the tuple must be - a singleton (i.e. it can contain only one element). - - encoding: the packet encoding (default is UTF-8) - - All byte strings in the data structure are assumed to use the - packet encoding. Unicode strings are automatically converted, - where necessary. - """ - - assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance" - if isinstance(params, Fault): - methodresponse = 1 - elif methodresponse and isinstance(params, tuple): - assert len(params) == 1, "response tuple must be a singleton" - - if not encoding: - encoding = "utf-8" - - if FastMarshaller: - m = FastMarshaller(encoding) - else: - m = Marshaller(encoding, allow_none) - - data = m.dumps(params) - - if encoding != "utf-8": - xmlheader = "\n" % str(encoding) - else: - xmlheader = "\n" # utf-8 is default - - # standard XML-RPC wrappings - if methodname: - # a method call - if not isinstance(methodname, str): - methodname = methodname.encode(encoding) - data = ( - xmlheader, - "\n" - "", methodname, "\n", - data, - "\n" - ) - elif methodresponse: - # a method response, or a fault structure - data = ( - xmlheader, - "\n", - data, - "\n" - ) - else: - return data # return as is - return str("").join(data) - -## -# Convert an XML-RPC packet to a Python object. If the XML-RPC packet -# represents a fault condition, this function raises a Fault exception. -# -# @param data An XML-RPC packet, given as an 8-bit string. -# @return A tuple containing the unpacked data, and the method name -# (None if not present). -# @see Fault - -def loads(data, use_datetime=False, use_builtin_types=False): - """data -> unmarshalled data, method name - - Convert an XML-RPC packet to unmarshalled data plus a method - name (None if not present). - - If the XML-RPC packet represents a fault condition, this function - raises a Fault exception. - """ - p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types) - p.feed(data) - p.close() - return u.close(), u.getmethodname() - -## -# Encode a string using the gzip content encoding such as specified by the -# Content-Encoding: gzip -# in the HTTP header, as described in RFC 1952 -# -# @param data the unencoded data -# @return the encoded data - -def gzip_encode(data): - """data -> gzip encoded data - - Encode data using the gzip content encoding as described in RFC 1952 - """ - if not gzip: - raise NotImplementedError - f = BytesIO() - gzf = gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) - gzf.write(data) - gzf.close() - encoded = f.getvalue() - f.close() - return encoded - -## -# Decode a string using the gzip content encoding such as specified by the -# Content-Encoding: gzip -# in the HTTP header, as described in RFC 1952 -# -# @param data The encoded data -# @return the unencoded data -# @raises ValueError if data is not correctly coded. - -def gzip_decode(data): - """gzip encoded data -> unencoded data - - Decode data using the gzip content encoding as described in RFC 1952 - """ - if not gzip: - raise NotImplementedError - f = BytesIO(data) - gzf = gzip.GzipFile(mode="rb", fileobj=f) - try: - decoded = gzf.read() - except IOError: - raise ValueError("invalid data") - f.close() - gzf.close() - return decoded - -## -# Return a decoded file-like object for the gzip encoding -# as described in RFC 1952. -# -# @param response A stream supporting a read() method -# @return a file-like object that the decoded data can be read() from - -class GzipDecodedResponse(gzip.GzipFile if gzip else object): - """a file-like object to decode a response encoded with the gzip - method, as described in RFC 1952. - """ - def __init__(self, response): - #response doesn't support tell() and read(), required by - #GzipFile - if not gzip: - raise NotImplementedError - self.io = BytesIO(response.read()) - gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io) - - def close(self): - gzip.GzipFile.close(self) - self.io.close() - - -# -------------------------------------------------------------------- -# request dispatcher - -class _Method(object): - # some magic to bind an XML-RPC method to an RPC server. - # supports "nested" methods (e.g. examples.getStateName) - def __init__(self, send, name): - self.__send = send - self.__name = name - def __getattr__(self, name): - return _Method(self.__send, "%s.%s" % (self.__name, name)) - def __call__(self, *args): - return self.__send(self.__name, args) - -## -# Standard transport class for XML-RPC over HTTP. -#

-# You can create custom transports by subclassing this method, and -# overriding selected methods. - -class Transport(object): - """Handles an HTTP transaction to an XML-RPC server.""" - - # client identifier (may be overridden) - user_agent = "Python-xmlrpc/%s" % __version__ - - #if true, we'll request gzip encoding - accept_gzip_encoding = True - - # if positive, encode request using gzip if it exceeds this threshold - # note that many server will get confused, so only use it if you know - # that they can decode such a request - encode_threshold = None #None = don't encode - - def __init__(self, use_datetime=False, use_builtin_types=False): - self._use_datetime = use_datetime - self._use_builtin_types = use_builtin_types - self._connection = (None, None) - self._extra_headers = [] - - ## - # Send a complete request, and parse the response. - # Retry request if a cached connection has disconnected. - # - # @param host Target host. - # @param handler Target PRC handler. - # @param request_body XML-RPC request body. - # @param verbose Debugging flag. - # @return Parsed response. - - def request(self, host, handler, request_body, verbose=False): - #retry request once if cached connection has gone cold - for i in (0, 1): - try: - return self.single_request(host, handler, request_body, verbose) - except socket.error as e: - if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, errno.EPIPE): - raise - except http_client.BadStatusLine: #close after we sent request - if i: - raise - - def single_request(self, host, handler, request_body, verbose=False): - # issue XML-RPC request - try: - http_conn = self.send_request(host, handler, request_body, verbose) - resp = http_conn.getresponse() - if resp.status == 200: - self.verbose = verbose - return self.parse_response(resp) - - except Fault: - raise - except Exception: - #All unexpected errors leave connection in - # a strange state, so we clear it. - self.close() - raise - - #We got an error response. - #Discard any response data and raise exception - if resp.getheader("content-length", ""): - resp.read() - raise ProtocolError( - host + handler, - resp.status, resp.reason, - dict(resp.getheaders()) - ) - - - ## - # Create parser. - # - # @return A 2-tuple containing a parser and a unmarshaller. - - def getparser(self): - # get parser and unmarshaller - return getparser(use_datetime=self._use_datetime, - use_builtin_types=self._use_builtin_types) - - ## - # Get authorization info from host parameter - # Host may be a string, or a (host, x509-dict) tuple; if a string, - # it is checked for a "user:pw@host" format, and a "Basic - # Authentication" header is added if appropriate. - # - # @param host Host descriptor (URL or (URL, x509 info) tuple). - # @return A 3-tuple containing (actual host, extra headers, - # x509 info). The header and x509 fields may be None. - - def get_host_info(self, host): - - x509 = {} - if isinstance(host, tuple): - host, x509 = host - - auth, host = urllib_parse.splituser(host) - - if auth: - auth = urllib_parse.unquote_to_bytes(auth) - auth = base64.encodebytes(auth).decode("utf-8") - auth = "".join(auth.split()) # get rid of whitespace - extra_headers = [ - ("Authorization", "Basic " + auth) - ] - else: - extra_headers = [] - - return host, extra_headers, x509 - - ## - # Connect to server. - # - # @param host Target host. - # @return An HTTPConnection object - - def make_connection(self, host): - #return an existing connection if possible. This allows - #HTTP/1.1 keep-alive. - if self._connection and host == self._connection[0]: - return self._connection[1] - # create a HTTP connection object from a host descriptor - chost, self._extra_headers, x509 = self.get_host_info(host) - self._connection = host, http_client.HTTPConnection(chost) - return self._connection[1] - - ## - # Clear any cached connection object. - # Used in the event of socket errors. - # - def close(self): - if self._connection[1]: - self._connection[1].close() - self._connection = (None, None) - - ## - # Send HTTP request. - # - # @param host Host descriptor (URL or (URL, x509 info) tuple). - # @param handler Targer RPC handler (a path relative to host) - # @param request_body The XML-RPC request body - # @param debug Enable debugging if debug is true. - # @return An HTTPConnection. - - def send_request(self, host, handler, request_body, debug): - connection = self.make_connection(host) - headers = self._extra_headers[:] - if debug: - connection.set_debuglevel(1) - if self.accept_gzip_encoding and gzip: - connection.putrequest("POST", handler, skip_accept_encoding=True) - headers.append(("Accept-Encoding", "gzip")) - else: - connection.putrequest("POST", handler) - headers.append(("Content-Type", "text/xml")) - headers.append(("User-Agent", self.user_agent)) - self.send_headers(connection, headers) - self.send_content(connection, request_body) - return connection - - ## - # Send request headers. - # This function provides a useful hook for subclassing - # - # @param connection httpConnection. - # @param headers list of key,value pairs for HTTP headers - - def send_headers(self, connection, headers): - for key, val in headers: - connection.putheader(key, val) - - ## - # Send request body. - # This function provides a useful hook for subclassing - # - # @param connection httpConnection. - # @param request_body XML-RPC request body. - - def send_content(self, connection, request_body): - #optionally encode the request - if (self.encode_threshold is not None and - self.encode_threshold < len(request_body) and - gzip): - connection.putheader("Content-Encoding", "gzip") - request_body = gzip_encode(request_body) - - connection.putheader("Content-Length", str(len(request_body))) - connection.endheaders(request_body) - - ## - # Parse response. - # - # @param file Stream. - # @return Response tuple and target method. - - def parse_response(self, response): - # read response data from httpresponse, and parse it - # Check for new http response object, otherwise it is a file object. - if hasattr(response, 'getheader'): - if response.getheader("Content-Encoding", "") == "gzip": - stream = GzipDecodedResponse(response) - else: - stream = response - else: - stream = response - - p, u = self.getparser() - - while 1: - data = stream.read(1024) - if not data: - break - if self.verbose: - print("body:", repr(data)) - p.feed(data) - - if stream is not response: - stream.close() - p.close() - - return u.close() - -## -# Standard transport class for XML-RPC over HTTPS. - -class SafeTransport(Transport): - """Handles an HTTPS transaction to an XML-RPC server.""" - - # FIXME: mostly untested - - def make_connection(self, host): - if self._connection and host == self._connection[0]: - return self._connection[1] - - if not hasattr(http_client, "HTTPSConnection"): - raise NotImplementedError( - "your version of http.client doesn't support HTTPS") - # create a HTTPS connection object from a host descriptor - # host may be a string, or a (host, x509-dict) tuple - chost, self._extra_headers, x509 = self.get_host_info(host) - self._connection = host, http_client.HTTPSConnection(chost, - None, **(x509 or {})) - return self._connection[1] - -## -# Standard server proxy. This class establishes a virtual connection -# to an XML-RPC server. -#

-# This class is available as ServerProxy and Server. New code should -# use ServerProxy, to avoid confusion. -# -# @def ServerProxy(uri, **options) -# @param uri The connection point on the server. -# @keyparam transport A transport factory, compatible with the -# standard transport class. -# @keyparam encoding The default encoding used for 8-bit strings -# (default is UTF-8). -# @keyparam verbose Use a true value to enable debugging output. -# (printed to standard output). -# @see Transport - -class ServerProxy(object): - """uri [,options] -> a logical connection to an XML-RPC server - - uri is the connection point on the server, given as - scheme://host/target. - - The standard implementation always supports the "http" scheme. If - SSL socket support is available (Python 2.0), it also supports - "https". - - If the target part and the slash preceding it are both omitted, - "/RPC2" is assumed. - - The following options can be given as keyword arguments: - - transport: a transport factory - encoding: the request encoding (default is UTF-8) - - All 8-bit strings passed to the server proxy are assumed to use - the given encoding. - """ - - def __init__(self, uri, transport=None, encoding=None, verbose=False, - allow_none=False, use_datetime=False, use_builtin_types=False): - # establish a "logical" server connection - - # get the url - type, uri = urllib_parse.splittype(uri) - if type not in ("http", "https"): - raise IOError("unsupported XML-RPC protocol") - self.__host, self.__handler = urllib_parse.splithost(uri) - if not self.__handler: - self.__handler = "/RPC2" - - if transport is None: - if type == "https": - handler = SafeTransport - else: - handler = Transport - transport = handler(use_datetime=use_datetime, - use_builtin_types=use_builtin_types) - self.__transport = transport - - self.__encoding = encoding or 'utf-8' - self.__verbose = verbose - self.__allow_none = allow_none - - def __close(self): - self.__transport.close() - - def __request(self, methodname, params): - # call a method on the remote server - - request = dumps(params, methodname, encoding=self.__encoding, - allow_none=self.__allow_none).encode(self.__encoding) - - response = self.__transport.request( - self.__host, - self.__handler, - request, - verbose=self.__verbose - ) - - if len(response) == 1: - response = response[0] - - return response - - def __repr__(self): - return ( - "" % - (self.__host, self.__handler) - ) - - __str__ = __repr__ - - def __getattr__(self, name): - # magic method dispatcher - return _Method(self.__request, name) - - # note: to call a remote object with an non-standard name, use - # result getattr(server, "strange-python-name")(args) - - def __call__(self, attr): - """A workaround to get special attributes on the ServerProxy - without interfering with the magic __getattr__ - """ - if attr == "close": - return self.__close - elif attr == "transport": - return self.__transport - raise AttributeError("Attribute %r not found" % (attr,)) - -# compatibility - -Server = ServerProxy - -# -------------------------------------------------------------------- -# test code - -if __name__ == "__main__": - - # simple test program (from the XML-RPC specification) - - # local server, available from Lib/xmlrpc/server.py - server = ServerProxy("http://localhost:8000") - - try: - print(server.currentTime.getCurrentTime()) - except Error as v: - print("ERROR", v) - - multi = MultiCall(server) - multi.getData() - multi.pow(2,9) - multi.add(1,2) - try: - for response in multi(): - print(response) - except Error as v: - print("ERROR", v) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/xmlrpc/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -# This directory is a Python package. diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/xmlrpc/server.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/xmlrpc/server.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/backports/xmlrpc/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/backports/xmlrpc/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,999 +0,0 @@ -r""" -Ported using Python-Future from the Python 3.3 standard library. - -XML-RPC Servers. - -This module can be used to create simple XML-RPC servers -by creating a server and either installing functions, a -class instance, or by extending the SimpleXMLRPCServer -class. - -It can also be used to handle XML-RPC requests in a CGI -environment using CGIXMLRPCRequestHandler. - -The Doc* classes can be used to create XML-RPC servers that -serve pydoc-style documentation in response to HTTP -GET requests. This documentation is dynamically generated -based on the functions and methods registered with the -server. - -A list of possible usage patterns follows: - -1. Install functions: - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_function(pow) -server.register_function(lambda x,y: x+y, 'add') -server.serve_forever() - -2. Install an instance: - -class MyFuncs: - def __init__(self): - # make all of the sys functions available through sys.func_name - import sys - self.sys = sys - def _listMethods(self): - # implement this method so that system.listMethods - # knows to advertise the sys methods - return list_public_methods(self) + \ - ['sys.' + method for method in list_public_methods(self.sys)] - def pow(self, x, y): return pow(x, y) - def add(self, x, y) : return x + y - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_introspection_functions() -server.register_instance(MyFuncs()) -server.serve_forever() - -3. Install an instance with custom dispatch method: - -class Math: - def _listMethods(self): - # this method must be present for system.listMethods - # to work - return ['add', 'pow'] - def _methodHelp(self, method): - # this method must be present for system.methodHelp - # to work - if method == 'add': - return "add(2,3) => 5" - elif method == 'pow': - return "pow(x, y[, z]) => number" - else: - # By convention, return empty - # string if no help is available - return "" - def _dispatch(self, method, params): - if method == 'pow': - return pow(*params) - elif method == 'add': - return params[0] + params[1] - else: - raise ValueError('bad method') - -server = SimpleXMLRPCServer(("localhost", 8000)) -server.register_introspection_functions() -server.register_instance(Math()) -server.serve_forever() - -4. Subclass SimpleXMLRPCServer: - -class MathServer(SimpleXMLRPCServer): - def _dispatch(self, method, params): - try: - # We are forcing the 'export_' prefix on methods that are - # callable through XML-RPC to prevent potential security - # problems - func = getattr(self, 'export_' + method) - except AttributeError: - raise Exception('method "%s" is not supported' % method) - else: - return func(*params) - - def export_add(self, x, y): - return x + y - -server = MathServer(("localhost", 8000)) -server.serve_forever() - -5. CGI script: - -server = CGIXMLRPCRequestHandler() -server.register_function(pow) -server.handle_request() -""" - -from __future__ import absolute_import, division, print_function, unicode_literals -from future.builtins import int, str - -# Written by Brian Quinlan (brian@sweetapp.com). -# Based on code written by Fredrik Lundh. - -from future.backports.xmlrpc.client import Fault, dumps, loads, gzip_encode, gzip_decode -from future.backports.http.server import BaseHTTPRequestHandler -import future.backports.http.server as http_server -from future.backports import socketserver -import sys -import os -import re -import pydoc -import inspect -import traceback -try: - import fcntl -except ImportError: - fcntl = None - -def resolve_dotted_attribute(obj, attr, allow_dotted_names=True): - """resolve_dotted_attribute(a, 'b.c.d') => a.b.c.d - - Resolves a dotted attribute name to an object. Raises - an AttributeError if any attribute in the chain starts with a '_'. - - If the optional allow_dotted_names argument is false, dots are not - supported and this function operates similar to getattr(obj, attr). - """ - - if allow_dotted_names: - attrs = attr.split('.') - else: - attrs = [attr] - - for i in attrs: - if i.startswith('_'): - raise AttributeError( - 'attempt to access private attribute "%s"' % i - ) - else: - obj = getattr(obj,i) - return obj - -def list_public_methods(obj): - """Returns a list of attribute strings, found in the specified - object, which represent callable attributes""" - - return [member for member in dir(obj) - if not member.startswith('_') and - callable(getattr(obj, member))] - -class SimpleXMLRPCDispatcher(object): - """Mix-in class that dispatches XML-RPC requests. - - This class is used to register XML-RPC method handlers - and then to dispatch them. This class doesn't need to be - instanced directly when used by SimpleXMLRPCServer but it - can be instanced when used by the MultiPathXMLRPCServer - """ - - def __init__(self, allow_none=False, encoding=None, - use_builtin_types=False): - self.funcs = {} - self.instance = None - self.allow_none = allow_none - self.encoding = encoding or 'utf-8' - self.use_builtin_types = use_builtin_types - - def register_instance(self, instance, allow_dotted_names=False): - """Registers an instance to respond to XML-RPC requests. - - Only one instance can be installed at a time. - - If the registered instance has a _dispatch method then that - method will be called with the name of the XML-RPC method and - its parameters as a tuple - e.g. instance._dispatch('add',(2,3)) - - If the registered instance does not have a _dispatch method - then the instance will be searched to find a matching method - and, if found, will be called. Methods beginning with an '_' - are considered private and will not be called by - SimpleXMLRPCServer. - - If a registered function matches a XML-RPC request, then it - will be called instead of the registered instance. - - If the optional allow_dotted_names argument is true and the - instance does not have a _dispatch method, method names - containing dots are supported and resolved, as long as none of - the name segments start with an '_'. - - *** SECURITY WARNING: *** - - Enabling the allow_dotted_names options allows intruders - to access your module's global variables and may allow - intruders to execute arbitrary code on your machine. Only - use this option on a secure, closed network. - - """ - - self.instance = instance - self.allow_dotted_names = allow_dotted_names - - def register_function(self, function, name=None): - """Registers a function to respond to XML-RPC requests. - - The optional name argument can be used to set a Unicode name - for the function. - """ - - if name is None: - name = function.__name__ - self.funcs[name] = function - - def register_introspection_functions(self): - """Registers the XML-RPC introspection methods in the system - namespace. - - see http://xmlrpc.usefulinc.com/doc/reserved.html - """ - - self.funcs.update({'system.listMethods' : self.system_listMethods, - 'system.methodSignature' : self.system_methodSignature, - 'system.methodHelp' : self.system_methodHelp}) - - def register_multicall_functions(self): - """Registers the XML-RPC multicall method in the system - namespace. - - see http://www.xmlrpc.com/discuss/msgReader$1208""" - - self.funcs.update({'system.multicall' : self.system_multicall}) - - def _marshaled_dispatch(self, data, dispatch_method = None, path = None): - """Dispatches an XML-RPC method from marshalled (XML) data. - - XML-RPC methods are dispatched from the marshalled (XML) data - using the _dispatch method and the result is returned as - marshalled data. For backwards compatibility, a dispatch - function can be provided as an argument (see comment in - SimpleXMLRPCRequestHandler.do_POST) but overriding the - existing method through subclassing is the preferred means - of changing method dispatch behavior. - """ - - try: - params, method = loads(data, use_builtin_types=self.use_builtin_types) - - # generate response - if dispatch_method is not None: - response = dispatch_method(method, params) - else: - response = self._dispatch(method, params) - # wrap response in a singleton tuple - response = (response,) - response = dumps(response, methodresponse=1, - allow_none=self.allow_none, encoding=self.encoding) - except Fault as fault: - response = dumps(fault, allow_none=self.allow_none, - encoding=self.encoding) - except: - # report exception back to server - exc_type, exc_value, exc_tb = sys.exc_info() - response = dumps( - Fault(1, "%s:%s" % (exc_type, exc_value)), - encoding=self.encoding, allow_none=self.allow_none, - ) - - return response.encode(self.encoding) - - def system_listMethods(self): - """system.listMethods() => ['add', 'subtract', 'multiple'] - - Returns a list of the methods supported by the server.""" - - methods = set(self.funcs.keys()) - if self.instance is not None: - # Instance can implement _listMethod to return a list of - # methods - if hasattr(self.instance, '_listMethods'): - methods |= set(self.instance._listMethods()) - # if the instance has a _dispatch method then we - # don't have enough information to provide a list - # of methods - elif not hasattr(self.instance, '_dispatch'): - methods |= set(list_public_methods(self.instance)) - return sorted(methods) - - def system_methodSignature(self, method_name): - """system.methodSignature('add') => [double, int, int] - - Returns a list describing the signature of the method. In the - above example, the add method takes two integers as arguments - and returns a double result. - - This server does NOT support system.methodSignature.""" - - # See http://xmlrpc.usefulinc.com/doc/sysmethodsig.html - - return 'signatures not supported' - - def system_methodHelp(self, method_name): - """system.methodHelp('add') => "Adds two integers together" - - Returns a string containing documentation for the specified method.""" - - method = None - if method_name in self.funcs: - method = self.funcs[method_name] - elif self.instance is not None: - # Instance can implement _methodHelp to return help for a method - if hasattr(self.instance, '_methodHelp'): - return self.instance._methodHelp(method_name) - # if the instance has a _dispatch method then we - # don't have enough information to provide help - elif not hasattr(self.instance, '_dispatch'): - try: - method = resolve_dotted_attribute( - self.instance, - method_name, - self.allow_dotted_names - ) - except AttributeError: - pass - - # Note that we aren't checking that the method actually - # be a callable object of some kind - if method is None: - return "" - else: - return pydoc.getdoc(method) - - def system_multicall(self, call_list): - """system.multicall([{'methodName': 'add', 'params': [2, 2]}, ...]) => \ -[[4], ...] - - Allows the caller to package multiple XML-RPC calls into a single - request. - - See http://www.xmlrpc.com/discuss/msgReader$1208 - """ - - results = [] - for call in call_list: - method_name = call['methodName'] - params = call['params'] - - try: - # XXX A marshalling error in any response will fail the entire - # multicall. If someone cares they should fix this. - results.append([self._dispatch(method_name, params)]) - except Fault as fault: - results.append( - {'faultCode' : fault.faultCode, - 'faultString' : fault.faultString} - ) - except: - exc_type, exc_value, exc_tb = sys.exc_info() - results.append( - {'faultCode' : 1, - 'faultString' : "%s:%s" % (exc_type, exc_value)} - ) - return results - - def _dispatch(self, method, params): - """Dispatches the XML-RPC method. - - XML-RPC calls are forwarded to a registered function that - matches the called XML-RPC method name. If no such function - exists then the call is forwarded to the registered instance, - if available. - - If the registered instance has a _dispatch method then that - method will be called with the name of the XML-RPC method and - its parameters as a tuple - e.g. instance._dispatch('add',(2,3)) - - If the registered instance does not have a _dispatch method - then the instance will be searched to find a matching method - and, if found, will be called. - - Methods beginning with an '_' are considered private and will - not be called. - """ - - func = None - try: - # check to see if a matching function has been registered - func = self.funcs[method] - except KeyError: - if self.instance is not None: - # check for a _dispatch method - if hasattr(self.instance, '_dispatch'): - return self.instance._dispatch(method, params) - else: - # call instance method directly - try: - func = resolve_dotted_attribute( - self.instance, - method, - self.allow_dotted_names - ) - except AttributeError: - pass - - if func is not None: - return func(*params) - else: - raise Exception('method "%s" is not supported' % method) - -class SimpleXMLRPCRequestHandler(BaseHTTPRequestHandler): - """Simple XML-RPC request handler class. - - Handles all HTTP POST requests and attempts to decode them as - XML-RPC requests. - """ - - # Class attribute listing the accessible path components; - # paths not on this list will result in a 404 error. - rpc_paths = ('/', '/RPC2') - - #if not None, encode responses larger than this, if possible - encode_threshold = 1400 #a common MTU - - #Override form StreamRequestHandler: full buffering of output - #and no Nagle. - wbufsize = -1 - disable_nagle_algorithm = True - - # a re to match a gzip Accept-Encoding - aepattern = re.compile(r""" - \s* ([^\s;]+) \s* #content-coding - (;\s* q \s*=\s* ([0-9\.]+))? #q - """, re.VERBOSE | re.IGNORECASE) - - def accept_encodings(self): - r = {} - ae = self.headers.get("Accept-Encoding", "") - for e in ae.split(","): - match = self.aepattern.match(e) - if match: - v = match.group(3) - v = float(v) if v else 1.0 - r[match.group(1)] = v - return r - - def is_rpc_path_valid(self): - if self.rpc_paths: - return self.path in self.rpc_paths - else: - # If .rpc_paths is empty, just assume all paths are legal - return True - - def do_POST(self): - """Handles the HTTP POST request. - - Attempts to interpret all HTTP POST requests as XML-RPC calls, - which are forwarded to the server's _dispatch method for handling. - """ - - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - try: - # Get arguments by reading body of request. - # We read this in chunks to avoid straining - # socket.read(); around the 10 or 15Mb mark, some platforms - # begin to have problems (bug #792570). - max_chunk_size = 10*1024*1024 - size_remaining = int(self.headers["content-length"]) - L = [] - while size_remaining: - chunk_size = min(size_remaining, max_chunk_size) - chunk = self.rfile.read(chunk_size) - if not chunk: - break - L.append(chunk) - size_remaining -= len(L[-1]) - data = b''.join(L) - - data = self.decode_request_content(data) - if data is None: - return #response has been sent - - # In previous versions of SimpleXMLRPCServer, _dispatch - # could be overridden in this class, instead of in - # SimpleXMLRPCDispatcher. To maintain backwards compatibility, - # check to see if a subclass implements _dispatch and dispatch - # using that method if present. - response = self.server._marshaled_dispatch( - data, getattr(self, '_dispatch', None), self.path - ) - except Exception as e: # This should only happen if the module is buggy - # internal error, report as HTTP server error - self.send_response(500) - - # Send information about the exception if requested - if hasattr(self.server, '_send_traceback_header') and \ - self.server._send_traceback_header: - self.send_header("X-exception", str(e)) - trace = traceback.format_exc() - trace = str(trace.encode('ASCII', 'backslashreplace'), 'ASCII') - self.send_header("X-traceback", trace) - - self.send_header("Content-length", "0") - self.end_headers() - else: - self.send_response(200) - self.send_header("Content-type", "text/xml") - if self.encode_threshold is not None: - if len(response) > self.encode_threshold: - q = self.accept_encodings().get("gzip", 0) - if q: - try: - response = gzip_encode(response) - self.send_header("Content-Encoding", "gzip") - except NotImplementedError: - pass - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - - def decode_request_content(self, data): - #support gzip encoding of request - encoding = self.headers.get("content-encoding", "identity").lower() - if encoding == "identity": - return data - if encoding == "gzip": - try: - return gzip_decode(data) - except NotImplementedError: - self.send_response(501, "encoding %r not supported" % encoding) - except ValueError: - self.send_response(400, "error decoding gzip content") - else: - self.send_response(501, "encoding %r not supported" % encoding) - self.send_header("Content-length", "0") - self.end_headers() - - def report_404 (self): - # Report a 404 error - self.send_response(404) - response = b'No such page' - self.send_header("Content-type", "text/plain") - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - - def log_request(self, code='-', size='-'): - """Selectively log an accepted request.""" - - if self.server.logRequests: - BaseHTTPRequestHandler.log_request(self, code, size) - -class SimpleXMLRPCServer(socketserver.TCPServer, - SimpleXMLRPCDispatcher): - """Simple XML-RPC server. - - Simple XML-RPC server that allows functions and a single instance - to be installed to handle requests. The default implementation - attempts to dispatch XML-RPC calls to the functions or instance - installed in the server. Override the _dispatch method inherited - from SimpleXMLRPCDispatcher to change this behavior. - """ - - allow_reuse_address = True - - # Warning: this is for debugging purposes only! Never set this to True in - # production code, as will be sending out sensitive information (exception - # and stack trace details) when exceptions are raised inside - # SimpleXMLRPCRequestHandler.do_POST - _send_traceback_header = False - - def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - self.logRequests = logRequests - - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) - socketserver.TCPServer.__init__(self, addr, requestHandler, bind_and_activate) - - # [Bug #1222790] If possible, set close-on-exec flag; if a - # method spawns a subprocess, the subprocess shouldn't have - # the listening socket open. - if fcntl is not None and hasattr(fcntl, 'FD_CLOEXEC'): - flags = fcntl.fcntl(self.fileno(), fcntl.F_GETFD) - flags |= fcntl.FD_CLOEXEC - fcntl.fcntl(self.fileno(), fcntl.F_SETFD, flags) - -class MultiPathXMLRPCServer(SimpleXMLRPCServer): - """Multipath XML-RPC Server - This specialization of SimpleXMLRPCServer allows the user to create - multiple Dispatcher instances and assign them to different - HTTP request paths. This makes it possible to run two or more - 'virtual XML-RPC servers' at the same port. - Make sure that the requestHandler accepts the paths in question. - """ - def __init__(self, addr, requestHandler=SimpleXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, allow_none, - encoding, bind_and_activate, use_builtin_types) - self.dispatchers = {} - self.allow_none = allow_none - self.encoding = encoding or 'utf-8' - - def add_dispatcher(self, path, dispatcher): - self.dispatchers[path] = dispatcher - return dispatcher - - def get_dispatcher(self, path): - return self.dispatchers[path] - - def _marshaled_dispatch(self, data, dispatch_method = None, path = None): - try: - response = self.dispatchers[path]._marshaled_dispatch( - data, dispatch_method, path) - except: - # report low level exception back to server - # (each dispatcher should have handled their own - # exceptions) - exc_type, exc_value = sys.exc_info()[:2] - response = dumps( - Fault(1, "%s:%s" % (exc_type, exc_value)), - encoding=self.encoding, allow_none=self.allow_none) - response = response.encode(self.encoding) - return response - -class CGIXMLRPCRequestHandler(SimpleXMLRPCDispatcher): - """Simple handler for XML-RPC data passed through CGI.""" - - def __init__(self, allow_none=False, encoding=None, use_builtin_types=False): - SimpleXMLRPCDispatcher.__init__(self, allow_none, encoding, use_builtin_types) - - def handle_xmlrpc(self, request_text): - """Handle a single XML-RPC request""" - - response = self._marshaled_dispatch(request_text) - - print('Content-Type: text/xml') - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def handle_get(self): - """Handle a single HTTP GET request. - - Default implementation indicates an error because - XML-RPC uses the POST method. - """ - - code = 400 - message, explain = BaseHTTPRequestHandler.responses[code] - - response = http_server.DEFAULT_ERROR_MESSAGE % \ - { - 'code' : code, - 'message' : message, - 'explain' : explain - } - response = response.encode('utf-8') - print('Status: %d %s' % (code, message)) - print('Content-Type: %s' % http_server.DEFAULT_ERROR_CONTENT_TYPE) - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def handle_request(self, request_text=None): - """Handle a single XML-RPC request passed through a CGI post method. - - If no XML data is given then it is read from stdin. The resulting - XML-RPC response is printed to stdout along with the correct HTTP - headers. - """ - - if request_text is None and \ - os.environ.get('REQUEST_METHOD', None) == 'GET': - self.handle_get() - else: - # POST data is normally available through stdin - try: - length = int(os.environ.get('CONTENT_LENGTH', None)) - except (ValueError, TypeError): - length = -1 - if request_text is None: - request_text = sys.stdin.read(length) - - self.handle_xmlrpc(request_text) - - -# ----------------------------------------------------------------------------- -# Self documenting XML-RPC Server. - -class ServerHTMLDoc(pydoc.HTMLDoc): - """Class used to generate pydoc HTML document for a server""" - - def markup(self, text, escape=None, funcs={}, classes={}, methods={}): - """Mark up some plain text, given a context of symbols to look for. - Each context dictionary maps object names to anchor names.""" - escape = escape or self.escape - results = [] - here = 0 - - # XXX Note that this regular expression does not allow for the - # hyperlinking of arbitrary strings being used as method - # names. Only methods with names consisting of word characters - # and '.'s are hyperlinked. - pattern = re.compile(r'\b((http|ftp)://\S+[\w/]|' - r'RFC[- ]?(\d+)|' - r'PEP[- ]?(\d+)|' - r'(self\.)?((?:\w|\.)+))\b') - while 1: - match = pattern.search(text, here) - if not match: break - start, end = match.span() - results.append(escape(text[here:start])) - - all, scheme, rfc, pep, selfdot, name = match.groups() - if scheme: - url = escape(all).replace('"', '"') - results.append('%s' % (url, url)) - elif rfc: - url = 'http://www.rfc-editor.org/rfc/rfc%d.txt' % int(rfc) - results.append('%s' % (url, escape(all))) - elif pep: - url = 'http://www.python.org/dev/peps/pep-%04d/' % int(pep) - results.append('%s' % (url, escape(all))) - elif text[end:end+1] == '(': - results.append(self.namelink(name, methods, funcs, classes)) - elif selfdot: - results.append('self.%s' % name) - else: - results.append(self.namelink(name, classes)) - here = end - results.append(escape(text[here:])) - return ''.join(results) - - def docroutine(self, object, name, mod=None, - funcs={}, classes={}, methods={}, cl=None): - """Produce HTML documentation for a function or method object.""" - - anchor = (cl and cl.__name__ or '') + '-' + name - note = '' - - title = '%s' % ( - self.escape(anchor), self.escape(name)) - - if inspect.ismethod(object): - args = inspect.getfullargspec(object) - # exclude the argument bound to the instance, it will be - # confusing to the non-Python user - argspec = inspect.formatargspec ( - args.args[1:], - args.varargs, - args.varkw, - args.defaults, - annotations=args.annotations, - formatvalue=self.formatvalue - ) - elif inspect.isfunction(object): - args = inspect.getfullargspec(object) - argspec = inspect.formatargspec( - args.args, args.varargs, args.varkw, args.defaults, - annotations=args.annotations, - formatvalue=self.formatvalue) - else: - argspec = '(...)' - - if isinstance(object, tuple): - argspec = object[0] or argspec - docstring = object[1] or "" - else: - docstring = pydoc.getdoc(object) - - decl = title + argspec + (note and self.grey( - '%s' % note)) - - doc = self.markup( - docstring, self.preformat, funcs, classes, methods) - doc = doc and '

%s
' % doc - return '
%s
%s
\n' % (decl, doc) - - def docserver(self, server_name, package_documentation, methods): - """Produce HTML documentation for an XML-RPC server.""" - - fdict = {} - for key, value in methods.items(): - fdict[key] = '#-' + key - fdict[value] = fdict[key] - - server_name = self.escape(server_name) - head = '%s' % server_name - result = self.heading(head, '#ffffff', '#7799ee') - - doc = self.markup(package_documentation, self.preformat, fdict) - doc = doc and '%s' % doc - result = result + '

%s

\n' % doc - - contents = [] - method_items = sorted(methods.items()) - for key, value in method_items: - contents.append(self.docroutine(value, key, funcs=fdict)) - result = result + self.bigsection( - 'Methods', '#ffffff', '#eeaa77', ''.join(contents)) - - return result - -class XMLRPCDocGenerator(object): - """Generates documentation for an XML-RPC server. - - This class is designed as mix-in and should not - be constructed directly. - """ - - def __init__(self): - # setup variables used for HTML documentation - self.server_name = 'XML-RPC Server Documentation' - self.server_documentation = \ - "This server exports the following methods through the XML-RPC "\ - "protocol." - self.server_title = 'XML-RPC Server Documentation' - - def set_server_title(self, server_title): - """Set the HTML title of the generated server documentation""" - - self.server_title = server_title - - def set_server_name(self, server_name): - """Set the name of the generated HTML server documentation""" - - self.server_name = server_name - - def set_server_documentation(self, server_documentation): - """Set the documentation string for the entire server.""" - - self.server_documentation = server_documentation - - def generate_html_documentation(self): - """generate_html_documentation() => html documentation for the server - - Generates HTML documentation for the server using introspection for - installed functions and instances that do not implement the - _dispatch method. Alternatively, instances can choose to implement - the _get_method_argstring(method_name) method to provide the - argument string used in the documentation and the - _methodHelp(method_name) method to provide the help text used - in the documentation.""" - - methods = {} - - for method_name in self.system_listMethods(): - if method_name in self.funcs: - method = self.funcs[method_name] - elif self.instance is not None: - method_info = [None, None] # argspec, documentation - if hasattr(self.instance, '_get_method_argstring'): - method_info[0] = self.instance._get_method_argstring(method_name) - if hasattr(self.instance, '_methodHelp'): - method_info[1] = self.instance._methodHelp(method_name) - - method_info = tuple(method_info) - if method_info != (None, None): - method = method_info - elif not hasattr(self.instance, '_dispatch'): - try: - method = resolve_dotted_attribute( - self.instance, - method_name - ) - except AttributeError: - method = method_info - else: - method = method_info - else: - assert 0, "Could not find method in self.functions and no "\ - "instance installed" - - methods[method_name] = method - - documenter = ServerHTMLDoc() - documentation = documenter.docserver( - self.server_name, - self.server_documentation, - methods - ) - - return documenter.page(self.server_title, documentation) - -class DocXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): - """XML-RPC and documentation request handler class. - - Handles all HTTP POST requests and attempts to decode them as - XML-RPC requests. - - Handles all HTTP GET requests and interprets them as requests - for documentation. - """ - - def do_GET(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - # Check that the path is legal - if not self.is_rpc_path_valid(): - self.report_404() - return - - response = self.server.generate_html_documentation().encode('utf-8') - self.send_response(200) - self.send_header("Content-type", "text/html") - self.send_header("Content-length", str(len(response))) - self.end_headers() - self.wfile.write(response) - -class DocXMLRPCServer( SimpleXMLRPCServer, - XMLRPCDocGenerator): - """XML-RPC and HTML documentation server. - - Adds the ability to serve server documentation to the capabilities - of SimpleXMLRPCServer. - """ - - def __init__(self, addr, requestHandler=DocXMLRPCRequestHandler, - logRequests=True, allow_none=False, encoding=None, - bind_and_activate=True, use_builtin_types=False): - SimpleXMLRPCServer.__init__(self, addr, requestHandler, logRequests, - allow_none, encoding, bind_and_activate, - use_builtin_types) - XMLRPCDocGenerator.__init__(self) - -class DocCGIXMLRPCRequestHandler( CGIXMLRPCRequestHandler, - XMLRPCDocGenerator): - """Handler for XML-RPC data and documentation requests passed through - CGI""" - - def handle_get(self): - """Handles the HTTP GET request. - - Interpret all HTTP GET requests as requests for server - documentation. - """ - - response = self.generate_html_documentation().encode('utf-8') - - print('Content-Type: text/html') - print('Content-Length: %d' % len(response)) - print() - sys.stdout.flush() - sys.stdout.buffer.write(response) - sys.stdout.buffer.flush() - - def __init__(self): - CGIXMLRPCRequestHandler.__init__(self) - XMLRPCDocGenerator.__init__(self) - - -if __name__ == '__main__': - import datetime - - class ExampleService: - def getData(self): - return '42' - - class currentTime: - @staticmethod - def getCurrentTime(): - return datetime.datetime.now() - - server = SimpleXMLRPCServer(("localhost", 8000)) - server.register_function(pow) - server.register_function(lambda x,y: x+y, 'add') - server.register_instance(ExampleService(), allow_dotted_names=True) - server.register_multicall_functions() - print('Serving XML-RPC on localhost port 8000') - print('It is advisable to run this example server within a secure, closed network.') - try: - server.serve_forever() - except KeyboardInterrupt: - print("\nKeyboard interrupt received, exiting.") - server.server_close() - sys.exit(0) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/disabled.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/disabled.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/disabled.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/disabled.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -""" -This disables builtin functions (and one exception class) which are -removed from Python 3.3. - -This module is designed to be used like this:: - - from future.builtins.disabled import * - -This disables the following obsolete Py2 builtin functions:: - - apply, cmp, coerce, execfile, file, input, long, - raw_input, reduce, reload, unicode, xrange - -We don't hack __builtin__, which is very fragile because it contaminates -imported modules too. Instead, we just create new functions with -the same names as the obsolete builtins from Python 2 which raise -NameError exceptions when called. - -Note that both ``input()`` and ``raw_input()`` are among the disabled -functions (in this module). Although ``input()`` exists as a builtin in -Python 3, the Python 2 ``input()`` builtin is unsafe to use because it -can lead to shell injection. Therefore we shadow it by default upon ``from -future.builtins.disabled import *``, in case someone forgets to import our -replacement ``input()`` somehow and expects Python 3 semantics. - -See the ``future.builtins.misc`` module for a working version of -``input`` with Python 3 semantics. - -(Note that callable() is not among the functions disabled; this was -reintroduced into Python 3.2.) - -This exception class is also disabled: - - StandardError - -""" - -from __future__ import division, absolute_import, print_function - -from future import utils - - -OBSOLETE_BUILTINS = ['apply', 'chr', 'cmp', 'coerce', 'execfile', 'file', - 'input', 'long', 'raw_input', 'reduce', 'reload', - 'unicode', 'xrange', 'StandardError'] - - -def disabled_function(name): - ''' - Returns a function that cannot be called - ''' - def disabled(*args, **kwargs): - ''' - A function disabled by the ``future`` module. This function is - no longer a builtin in Python 3. - ''' - raise NameError('obsolete Python 2 builtin {0} is disabled'.format(name)) - return disabled - - -if not utils.PY3: - for fname in OBSOLETE_BUILTINS: - locals()[fname] = disabled_function(fname) - __all__ = OBSOLETE_BUILTINS -else: - __all__ = [] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -""" -A module that brings in equivalents of the new and modified Python 3 -builtins into Py2. Has no effect on Py3. - -See the docs `here `_ -(``docs/what-else.rst``) for more information. - -""" - -from future.builtins.iterators import (filter, map, zip) -# The isinstance import is no longer needed. We provide it only for -# backward-compatibility with future v0.8.2. It will be removed in future v1.0. -from future.builtins.misc import (ascii, chr, hex, input, isinstance, next, - oct, open, pow, round, super) -from future.utils import PY3 - -if PY3: - import builtins - bytes = builtins.bytes - dict = builtins.dict - int = builtins.int - list = builtins.list - object = builtins.object - range = builtins.range - str = builtins.str - __all__ = [] -else: - from future.types import (newbytes as bytes, - newdict as dict, - newint as int, - newlist as list, - newobject as object, - newrange as range, - newstr as str) -from future import utils - - -if not utils.PY3: - # We only import names that shadow the builtins on Py2. No other namespace - # pollution on Py2. - - # Only shadow builtins on Py2; no new names - __all__ = ['filter', 'map', 'zip', - 'ascii', 'chr', 'hex', 'input', 'next', 'oct', 'open', 'pow', - 'round', 'super', - 'bytes', 'dict', 'int', 'list', 'object', 'range', 'str', - ] - -else: - # No namespace pollution on Py3 - __all__ = [] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/iterators.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/iterators.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/iterators.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/iterators.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -""" -This module is designed to be used as follows:: - - from future.builtins.iterators import * - -And then, for example:: - - for i in range(10**15): - pass - - for (a, b) in zip(range(10**15), range(-10**15, 0)): - pass - -Note that this is standard Python 3 code, plus some imports that do -nothing on Python 3. - -The iterators this brings in are:: - -- ``range`` -- ``filter`` -- ``map`` -- ``zip`` - -On Python 2, ``range`` is a pure-Python backport of Python 3's ``range`` -iterator with slicing support. The other iterators (``filter``, ``map``, -``zip``) are from the ``itertools`` module on Python 2. On Python 3 these -are available in the module namespace but not exported for * imports via -__all__ (zero no namespace pollution). - -Note that these are also available in the standard library -``future_builtins`` module on Python 2 -- but not Python 3, so using -the standard library version is not portable, nor anywhere near complete. -""" - -from __future__ import division, absolute_import, print_function - -import itertools -from future import utils - -if not utils.PY3: - filter = itertools.ifilter - map = itertools.imap - from future.types import newrange as range - zip = itertools.izip - __all__ = ['filter', 'map', 'range', 'zip'] -else: - import builtins - filter = builtins.filter - map = builtins.map - range = builtins.range - zip = builtins.zip - __all__ = [] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/misc.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/misc.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/misc.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/misc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -""" -A module that brings in equivalents of various modified Python 3 builtins -into Py2. Has no effect on Py3. - -The builtin functions are: - -- ``ascii`` (from Py2's future_builtins module) -- ``hex`` (from Py2's future_builtins module) -- ``oct`` (from Py2's future_builtins module) -- ``chr`` (equivalent to ``unichr`` on Py2) -- ``input`` (equivalent to ``raw_input`` on Py2) -- ``next`` (calls ``__next__`` if it exists, else ``next`` method) -- ``open`` (equivalent to io.open on Py2) -- ``super`` (backport of Py3's magic zero-argument super() function -- ``round`` (new "Banker's Rounding" behaviour from Py3) - -``isinstance`` is also currently exported for backwards compatibility -with v0.8.2, although this has been deprecated since v0.9. - - -input() -------- -Like the new ``input()`` function from Python 3 (without eval()), except -that it returns bytes. Equivalent to Python 2's ``raw_input()``. - -Warning: By default, importing this module *removes* the old Python 2 -input() function entirely from ``__builtin__`` for safety. This is -because forgetting to import the new ``input`` from ``future`` might -otherwise lead to a security vulnerability (shell injection) on Python 2. - -To restore it, you can retrieve it yourself from -``__builtin__._old_input``. - -Fortunately, ``input()`` seems to be seldom used in the wild in Python -2... - -""" - -from future import utils - - -if utils.PY2: - from io import open - from future_builtins import ascii, oct, hex - from __builtin__ import unichr as chr, pow as _builtin_pow - import __builtin__ - - # Only for backward compatibility with future v0.8.2: - isinstance = __builtin__.isinstance - - # Warning: Python 2's input() is unsafe and MUST not be able to be used - # accidentally by someone who expects Python 3 semantics but forgets - # to import it on Python 2. Versions of ``future`` prior to 0.11 - # deleted it from __builtin__. Now we keep in __builtin__ but shadow - # the name like all others. Just be sure to import ``input``. - - input = raw_input - - from future.builtins.newnext import newnext as next - from future.builtins.newround import newround as round - from future.builtins.newsuper import newsuper as super - from future.types.newint import newint - - _SENTINEL = object() - - def pow(x, y, z=_SENTINEL): - """ - pow(x, y[, z]) -> number - - With two arguments, equivalent to x**y. With three arguments, - equivalent to (x**y) % z, but may be more efficient (e.g. for ints). - """ - # Handle newints - if isinstance(x, newint): - x = long(x) - if isinstance(y, newint): - y = long(y) - if isinstance(z, newint): - z = long(z) - - try: - if z == _SENTINEL: - return _builtin_pow(x, y) - else: - return _builtin_pow(x, y, z) - except ValueError: - if z == _SENTINEL: - return _builtin_pow(x+0j, y) - else: - return _builtin_pow(x+0j, y, z) - - # ``future`` doesn't support Py3.0/3.1. If we ever did, we'd add this: - # callable = __builtin__.callable - - __all__ = ['ascii', 'chr', 'hex', 'input', 'isinstance', 'next', 'oct', - 'open', 'pow', 'round', 'super'] - -else: - import builtins - ascii = builtins.ascii - chr = builtins.chr - hex = builtins.hex - input = builtins.input - next = builtins.next - # Only for backward compatibility with future v0.8.2: - isinstance = builtins.isinstance - oct = builtins.oct - open = builtins.open - pow = builtins.pow - round = builtins.round - super = builtins.super - - __all__ = [] - - # The callable() function was removed from Py3.0 and 3.1 and - # reintroduced into Py3.2+. ``future`` doesn't support Py3.0/3.1. If we ever - # did, we'd add this: - # try: - # callable = builtins.callable - # except AttributeError: - # # Definition from Pandas - # def callable(obj): - # return any("__call__" in klass.__dict__ for klass in type(obj).__mro__) - # __all__.append('callable') diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/newnext.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/newnext.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/newnext.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/newnext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -''' -This module provides a newnext() function in Python 2 that mimics the -behaviour of ``next()`` in Python 3, falling back to Python 2's behaviour for -compatibility if this fails. - -``newnext(iterator)`` calls the iterator's ``__next__()`` method if it exists. If this -doesn't exist, it falls back to calling a ``next()`` method. - -For example: - - >>> class Odds(object): - ... def __init__(self, start=1): - ... self.value = start - 2 - ... def __next__(self): # note the Py3 interface - ... self.value += 2 - ... return self.value - ... def __iter__(self): - ... return self - ... - >>> iterator = Odds() - >>> next(iterator) - 1 - >>> next(iterator) - 3 - -If you are defining your own custom iterator class as above, it is preferable -to explicitly decorate the class with the @implements_iterator decorator from -``future.utils`` as follows: - - >>> @implements_iterator - ... class Odds(object): - ... # etc - ... pass - -This next() function is primarily for consuming iterators defined in Python 3 -code elsewhere that we would like to run on Python 2 or 3. -''' - -_builtin_next = next - -_SENTINEL = object() - -def newnext(iterator, default=_SENTINEL): - """ - next(iterator[, default]) - - Return the next item from the iterator. If default is given and the iterator - is exhausted, it is returned instead of raising StopIteration. - """ - - # args = [] - # if default is not _SENTINEL: - # args.append(default) - try: - try: - return iterator.__next__() - except AttributeError: - try: - return iterator.next() - except AttributeError: - raise TypeError("'{0}' object is not an iterator".format( - iterator.__class__.__name__)) - except StopIteration as e: - if default is _SENTINEL: - raise e - else: - return default - - -__all__ = ['newnext'] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/newround.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/newround.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/newround.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/newround.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -""" -``python-future``: pure Python implementation of Python 3 round(). -""" - -from future.utils import PYPY, PY26, bind_method - -# Use the decimal module for simplicity of implementation (and -# hopefully correctness). -from decimal import Decimal, ROUND_HALF_EVEN - - -def newround(number, ndigits=None): - """ - See Python 3 documentation: uses Banker's Rounding. - - Delegates to the __round__ method if for some reason this exists. - - If not, rounds a number to a given precision in decimal digits (default - 0 digits). This returns an int when called with one argument, - otherwise the same type as the number. ndigits may be negative. - - See the test_round method in future/tests/test_builtins.py for - examples. - """ - return_int = False - if ndigits is None: - return_int = True - ndigits = 0 - if hasattr(number, '__round__'): - return number.__round__(ndigits) - - if ndigits < 0: - raise NotImplementedError('negative ndigits not supported yet') - exponent = Decimal('10') ** (-ndigits) - - if PYPY: - # Work around issue #24: round() breaks on PyPy with NumPy's types - if 'numpy' in repr(type(number)): - number = float(number) - - if not PY26: - d = Decimal.from_float(number).quantize(exponent, - rounding=ROUND_HALF_EVEN) - else: - d = from_float_26(number).quantize(exponent, rounding=ROUND_HALF_EVEN) - - if return_int: - return int(d) - else: - return float(d) - - -### From Python 2.7's decimal.py. Only needed to support Py2.6: - -def from_float_26(f): - """Converts a float to a decimal number, exactly. - - Note that Decimal.from_float(0.1) is not the same as Decimal('0.1'). - Since 0.1 is not exactly representable in binary floating point, the - value is stored as the nearest representable value which is - 0x1.999999999999ap-4. The exact equivalent of the value in decimal - is 0.1000000000000000055511151231257827021181583404541015625. - - >>> Decimal.from_float(0.1) - Decimal('0.1000000000000000055511151231257827021181583404541015625') - >>> Decimal.from_float(float('nan')) - Decimal('NaN') - >>> Decimal.from_float(float('inf')) - Decimal('Infinity') - >>> Decimal.from_float(-float('inf')) - Decimal('-Infinity') - >>> Decimal.from_float(-0.0) - Decimal('-0') - - """ - import math as _math - from decimal import _dec_from_triple # only available on Py2.6 and Py2.7 (not 3.3) - - if isinstance(f, (int, long)): # handle integer inputs - return Decimal(f) - if _math.isinf(f) or _math.isnan(f): # raises TypeError if not a float - return Decimal(repr(f)) - if _math.copysign(1.0, f) == 1.0: - sign = 0 - else: - sign = 1 - n, d = abs(f).as_integer_ratio() - # int.bit_length() method doesn't exist on Py2.6: - def bit_length(d): - if d != 0: - return len(bin(abs(d))) - 2 - else: - return 0 - k = bit_length(d) - 1 - result = _dec_from_triple(sign, str(n*5**k), -k) - return result - - -__all__ = ['newround'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/newsuper.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/newsuper.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/builtins/newsuper.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/builtins/newsuper.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,114 +0,0 @@ -''' -This module provides a newsuper() function in Python 2 that mimics the -behaviour of super() in Python 3. It is designed to be used as follows: - - from __future__ import division, absolute_import, print_function - from future.builtins import super - -And then, for example: - - class VerboseList(list): - def append(self, item): - print('Adding an item') - super().append(item) # new simpler super() function - -Importing this module on Python 3 has no effect. - -This is based on (i.e. almost identical to) Ryan Kelly's magicsuper -module here: - - https://github.com/rfk/magicsuper.git - -Excerpts from Ryan's docstring: - - "Of course, you can still explicitly pass in the arguments if you want - to do something strange. Sometimes you really do want that, e.g. to - skip over some classes in the method resolution order. - - "How does it work? By inspecting the calling frame to determine the - function object being executed and the object on which it's being - called, and then walking the object's __mro__ chain to find out where - that function was defined. Yuck, but it seems to work..." -''' - -from __future__ import absolute_import -import sys -from types import FunctionType - -from future.utils import PY3, PY26 - - -_builtin_super = super - -_SENTINEL = object() - -def newsuper(typ=_SENTINEL, type_or_obj=_SENTINEL, framedepth=1): - '''Like builtin super(), but capable of magic. - - This acts just like the builtin super() function, but if called - without any arguments it attempts to infer them at runtime. - ''' - # Infer the correct call if used without arguments. - if typ is _SENTINEL: - # We'll need to do some frame hacking. - f = sys._getframe(framedepth) - - try: - # Get the function's first positional argument. - type_or_obj = f.f_locals[f.f_code.co_varnames[0]] - except (IndexError, KeyError,): - raise RuntimeError('super() used in a function with no args') - - try: - # Get the MRO so we can crawl it. - mro = type_or_obj.__mro__ - except (AttributeError, RuntimeError): # see issue #160 - try: - mro = type_or_obj.__class__.__mro__ - except AttributeError: - raise RuntimeError('super() used with a non-newstyle class') - - # A ``for...else`` block? Yes! It's odd, but useful. - # If unfamiliar with for...else, see: - # - # http://psung.blogspot.com/2007/12/for-else-in-python.html - for typ in mro: - # Find the class that owns the currently-executing method. - for meth in typ.__dict__.values(): - # Drill down through any wrappers to the underlying func. - # This handles e.g. classmethod() and staticmethod(). - try: - while not isinstance(meth,FunctionType): - if isinstance(meth, property): - # Calling __get__ on the property will invoke - # user code which might throw exceptions or have - # side effects - meth = meth.fget - else: - try: - meth = meth.__func__ - except AttributeError: - meth = meth.__get__(type_or_obj, typ) - except (AttributeError, TypeError): - continue - if meth.func_code is f.f_code: - break # Aha! Found you. - else: - continue # Not found! Move onto the next class in MRO. - break # Found! Break out of the search loop. - else: - raise RuntimeError('super() called outside a method') - - # Dispatch to builtin super(). - if type_or_obj is not _SENTINEL: - return _builtin_super(typ, type_or_obj) - return _builtin_super(typ) - - -def superm(*args, **kwds): - f = sys._getframe(1) - nm = f.f_code.co_name - return getattr(newsuper(framedepth=2),nm)(*args, **kwds) - - -__all__ = ['newsuper'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -""" -future: Easy, safe support for Python 2/3 compatibility -======================================================= - -``future`` is the missing compatibility layer between Python 2 and Python -3. It allows you to use a single, clean Python 3.x-compatible codebase to -support both Python 2 and Python 3 with minimal overhead. - -It is designed to be used as follows:: - - from __future__ import (absolute_import, division, - print_function, unicode_literals) - from builtins import ( - bytes, dict, int, list, object, range, str, - ascii, chr, hex, input, next, oct, open, - pow, round, super, - filter, map, zip) - -followed by predominantly standard, idiomatic Python 3 code that then runs -similarly on Python 2.6/2.7 and Python 3.3+. - -The imports have no effect on Python 3. On Python 2, they shadow the -corresponding builtins, which normally have different semantics on Python 3 -versus 2, to provide their Python 3 semantics. - - -Standard library reorganization -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``future`` supports the standard library reorganization (PEP 3108) through the -following Py3 interfaces: - - >>> # Top-level packages with Py3 names provided on Py2: - >>> import html.parser - >>> import queue - >>> import tkinter.dialog - >>> import xmlrpc.client - >>> # etc. - - >>> # Aliases provided for extensions to existing Py2 module names: - >>> from future.standard_library import install_aliases - >>> install_aliases() - - >>> from collections import Counter, OrderedDict # backported to Py2.6 - >>> from collections import UserDict, UserList, UserString - >>> import urllib.request - >>> from itertools import filterfalse, zip_longest - >>> from subprocess import getoutput, getstatusoutput - - -Automatic conversion --------------------- - -An included script called `futurize -`_ aids in converting -code (from either Python 2 or Python 3) to code compatible with both -platforms. It is similar to ``python-modernize`` but goes further in -providing Python 3 compatibility through the use of the backported types -and builtin functions in ``future``. - - -Documentation -------------- - -See: http://python-future.org - - -Credits -------- - -:Author: Ed Schofield -:Sponsor: Python Charmers Pty Ltd, Australia, and Python Charmers Pte - Ltd, Singapore. http://pythoncharmers.com -:Others: See docs/credits.rst or http://python-future.org/credits.html - - -Licensing ---------- -Copyright 2013-2016 Python Charmers Pty Ltd, Australia. -The software is distributed under an MIT licence. See LICENSE.txt. - -""" - -__title__ = 'future' -__author__ = 'Ed Schofield' -__license__ = 'MIT' -__copyright__ = 'Copyright 2013-2016 Python Charmers Pty Ltd' -__ver_major__ = 0 -__ver_minor__ = 16 -__ver_patch__ = 0 -__ver_sub__ = '' -__version__ = "%d.%d.%d%s" % (__ver_major__, __ver_minor__, - __ver_patch__, __ver_sub__) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/builtins.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/builtins.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from builtins import * -else: - __future_module__ = True - from __builtin__ import * - # Overwrite any old definitions with the equivalent future.builtins ones: - from future.builtins import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/collections.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/collections.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/collections.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/collections.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -from __future__ import absolute_import -import sys - -from future.utils import PY2, PY26 -__future_module__ = True - -from collections import * - -if PY2: - from UserDict import UserDict - from UserList import UserList - from UserString import UserString - -if PY26: - from future.backports.misc import OrderedDict, Counter - -if sys.version_info < (3, 3): - from future.backports.misc import ChainMap, _count_elements diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/configparser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/configparser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/configparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/configparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY2 - -if PY2: - from ConfigParser import * -else: - from configparser import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/copyreg.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/copyreg.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/copyreg.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/copyreg.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from copyreg import * -else: - __future_module__ = True - from copy_reg import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/dumb.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/dumb.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/dumb.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/dumb.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from dbm.dumb import * -else: - __future_module__ = True - from dumbdbm import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/gnu.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/gnu.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/gnu.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/gnu.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from dbm.gnu import * -else: - __future_module__ = True - from gdbm import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from dbm import * -else: - __future_module__ = True - from whichdb import * - from anydbm import * - -# Py3.3's dbm/__init__.py imports ndbm but doesn't expose it via __all__. -# In case some (badly written) code depends on dbm.ndbm after import dbm, -# we simulate this: -if PY3: - from dbm import ndbm -else: - try: - from future.moves.dbm import ndbm - except ImportError: - ndbm = None diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/ndbm.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/ndbm.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/dbm/ndbm.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/dbm/ndbm.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from dbm.ndbm import * -else: - __future_module__ = True - from dbm import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/_dummy_thread.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/_dummy_thread.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/_dummy_thread.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/_dummy_thread.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _dummy_thread import * -else: - __future_module__ = True - from dummy_thread import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/html/entities.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/html/entities.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/html/entities.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/html/entities.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from html.entities import * -else: - __future_module__ = True - from htmlentitydefs import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/html/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/html/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/html/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/html/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 -__future_module__ = True - -if PY3: - from html import * -else: - # cgi.escape isn't good enough for the single Py3.3 html test to pass. - # Define it inline here instead. From the Py3.4 stdlib. Note that the - # html.escape() function from the Py3.3 stdlib is not suitable for use on - # Py2.x. - """ - General functions for HTML manipulation. - """ - - def escape(s, quote=True): - """ - Replace special characters "&", "<" and ">" to HTML-safe sequences. - If the optional flag quote is true (the default), the quotation mark - characters, both double quote (") and single quote (') characters are also - translated. - """ - s = s.replace("&", "&") # Must be done first! - s = s.replace("<", "<") - s = s.replace(">", ">") - if quote: - s = s.replace('"', """) - s = s.replace('\'', "'") - return s - - __all__ = ['escape'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/html/parser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/html/parser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/html/parser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/html/parser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 -__future_module__ = True - -if PY3: - from html.parser import * -else: - from HTMLParser import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/client.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/client.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from future.utils import PY3 - -if PY3: - from http.client import * -else: - from httplib import * - from httplib import HTTPMessage - __future_module__ = True diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/cookiejar.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/cookiejar.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/cookiejar.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/cookiejar.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from http.cookiejar import * -else: - __future_module__ = True - from cookielib import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/cookies.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/cookies.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/cookies.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/cookies.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from http.cookies import * -else: - __future_module__ = True - from Cookie import * - from Cookie import Morsel # left out of __all__ on Py2.7! diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -from future.utils import PY3 - -if not PY3: - __future_module__ = True diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/server.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/server.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/http/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/http/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from http.server import * -else: - __future_module__ = True - from BaseHTTPServer import * - from CGIHTTPServer import * - from SimpleHTTPServer import * - try: - from CGIHTTPServer import _url_collapse_path # needed for a test - except ImportError: - try: - # Python 2.7.0 to 2.7.3 - from CGIHTTPServer import ( - _url_collapse_path_split as _url_collapse_path) - except ImportError: - # Doesn't exist on Python 2.6.x. Ignore it. - pass diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -# future.moves package -from __future__ import absolute_import -import sys -__future_module__ = True -from future.standard_library import import_top_level_modules - -if sys.version_info[0] == 3: - import_top_level_modules() diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/itertools.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/itertools.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/itertools.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/itertools.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import - -from itertools import * -try: - zip_longest = izip_longest - filterfalse = ifilterfalse -except NameError: - pass diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/_markupbase.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/_markupbase.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/_markupbase.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/_markupbase.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _markupbase import * -else: - __future_module__ = True - from markupbase import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/pickle.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/pickle.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/pickle.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/pickle.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from pickle import * -else: - __future_module__ = True - try: - from cPickle import * - except ImportError: - from pickle import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/queue.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/queue.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/queue.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/queue.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from queue import * -else: - __future_module__ = True - from Queue import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/reprlib.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/reprlib.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/reprlib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/reprlib.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from reprlib import * -else: - __future_module__ = True - from repr import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/socketserver.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/socketserver.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/socketserver.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/socketserver.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from socketserver import * -else: - __future_module__ = True - from SocketServer import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/subprocess.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/subprocess.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/subprocess.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/subprocess.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY2, PY26 - -from subprocess import * - -if PY2: - __future_module__ = True - from commands import getoutput, getstatusoutput - -if PY26: - from future.backports.misc import check_output diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/sys.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/sys.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/sys.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/sys.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY2 - -from sys import * - -if PY2: - from __builtin__ import intern diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/test/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/test/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/test/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/test/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if not PY3: - __future_module__ = True diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/test/support.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/test/support.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/test/support.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/test/support.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.standard_library import suspend_hooks -from future.utils import PY3 - -if PY3: - from test.support import * -else: - __future_module__ = True - with suspend_hooks(): - from test.test_support import * - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/_thread.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/_thread.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/_thread.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/_thread.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from _thread import * -else: - __future_module__ = True - from thread import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/colorchooser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.colorchooser import * -else: - try: - from tkColorChooser import * - except ImportError: - raise ImportError('The tkColorChooser module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/commondialog.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/commondialog.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/commondialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/commondialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.commondialog import * -else: - try: - from tkCommonDialog import * - except ImportError: - raise ImportError('The tkCommonDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/constants.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/constants.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/constants.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/constants.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.constants import * -else: - try: - from Tkconstants import * - except ImportError: - raise ImportError('The Tkconstants module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/dialog.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/dialog.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/dialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/dialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dialog import * -else: - try: - from Dialog import * - except ImportError: - raise ImportError('The Dialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/dnd.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/dnd.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/dnd.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/dnd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.dnd import * -else: - try: - from Tkdnd import * - except ImportError: - raise ImportError('The Tkdnd module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/filedialog.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/filedialog.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/filedialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/filedialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.filedialog import * -else: - try: - from FileDialog import * - except ImportError: - raise ImportError('The FileDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/font.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/font.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/font.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/font.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.font import * -else: - try: - from tkFont import * - except ImportError: - raise ImportError('The tkFont module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 -__future_module__ = True - -if not PY3: - from Tkinter import * - from Tkinter import (_cnfmerge, _default_root, _flatten, _join, _setit, - _splitdict, _stringify, _support_default_root, _test, - _tkinter) -else: - from tkinter import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/messagebox.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/messagebox.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/messagebox.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/messagebox.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.messagebox import * -else: - try: - from tkMessageBox import * - except ImportError: - raise ImportError('The tkMessageBox module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/scrolledtext.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.scrolledtext import * -else: - try: - from ScrolledText import * - except ImportError: - raise ImportError('The ScrolledText module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/simpledialog.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.simpledialog import * -else: - try: - from SimpleDialog import * - except ImportError: - raise ImportError('The SimpleDialog module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/tix.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/tix.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/tix.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/tix.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.tix import * -else: - try: - from Tix import * - except ImportError: - raise ImportError('The Tix module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/ttk.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/ttk.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/tkinter/ttk.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/tkinter/ttk.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from __future__ import absolute_import - -from future.utils import PY3 - -if PY3: - from tkinter.ttk import * -else: - try: - from ttk import * - except ImportError: - raise ImportError('The ttk module is missing. Does your Py2 ' - 'installation include tkinter?') - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/error.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/error.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/error.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/error.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -from __future__ import absolute_import -from future.standard_library import suspend_hooks - -from future.utils import PY3 - -if PY3: - from urllib.error import * -else: - __future_module__ = True - - # We use this method to get at the original Py2 urllib before any renaming magic - # ContentTooShortError = sys.py2_modules['urllib'].ContentTooShortError - - with suspend_hooks(): - from urllib import ContentTooShortError - from urllib2 import URLError, HTTPError diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if not PY3: - __future_module__ = True - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/parse.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/parse.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/parse.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/parse.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -from __future__ import absolute_import -from future.standard_library import suspend_hooks - -from future.utils import PY3 - -if PY3: - from urllib.parse import * -else: - __future_module__ = True - from urlparse import (ParseResult, SplitResult, parse_qs, parse_qsl, - urldefrag, urljoin, urlparse, urlsplit, - urlunparse, urlunsplit) - - # we use this method to get at the original py2 urllib before any renaming - # quote = sys.py2_modules['urllib'].quote - # quote_plus = sys.py2_modules['urllib'].quote_plus - # unquote = sys.py2_modules['urllib'].unquote - # unquote_plus = sys.py2_modules['urllib'].unquote_plus - # urlencode = sys.py2_modules['urllib'].urlencode - # splitquery = sys.py2_modules['urllib'].splitquery - - with suspend_hooks(): - from urllib import (quote, - quote_plus, - unquote, - unquote_plus, - urlencode, - splitquery) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/request.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/request.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/request.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/request.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -from __future__ import absolute_import - -from future.standard_library import suspend_hooks -from future.utils import PY3 - -if PY3: - from urllib.request import * - # This aren't in __all__: - from urllib.request import (getproxies, - pathname2url, - proxy_bypass, - quote, - request_host, - splitattr, - splithost, - splitpasswd, - splitport, - splitquery, - splittag, - splittype, - splituser, - splitvalue, - thishost, - to_bytes, - unquote, - unwrap, - url2pathname, - urlcleanup, - urljoin, - urlopen, - urlparse, - urlretrieve, - urlsplit, - urlunparse) -else: - __future_module__ = True - with suspend_hooks(): - from urllib import * - from urllib2 import * - from urlparse import * - - # Rename: - from urllib import toBytes # missing from __all__ on Py2.6 - to_bytes = toBytes - - # from urllib import (pathname2url, - # url2pathname, - # getproxies, - # urlretrieve, - # urlcleanup, - # URLopener, - # FancyURLopener, - # proxy_bypass) - - # from urllib2 import ( - # AbstractBasicAuthHandler, - # AbstractDigestAuthHandler, - # BaseHandler, - # CacheFTPHandler, - # FileHandler, - # FTPHandler, - # HTTPBasicAuthHandler, - # HTTPCookieProcessor, - # HTTPDefaultErrorHandler, - # HTTPDigestAuthHandler, - # HTTPErrorProcessor, - # HTTPHandler, - # HTTPPasswordMgr, - # HTTPPasswordMgrWithDefaultRealm, - # HTTPRedirectHandler, - # HTTPSHandler, - # URLError, - # build_opener, - # install_opener, - # OpenerDirector, - # ProxyBasicAuthHandler, - # ProxyDigestAuthHandler, - # ProxyHandler, - # Request, - # UnknownHandler, - # urlopen, - # ) - - # from urlparse import ( - # urldefrag - # urljoin, - # urlparse, - # urlunparse, - # urlsplit, - # urlunsplit, - # parse_qs, - # parse_q" - # ) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/response.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/response.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/response.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/response.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -from future import standard_library -from future.utils import PY3 - -if PY3: - from urllib.response import * -else: - __future_module__ = True - with standard_library.suspend_hooks(): - from urllib import (addbase, - addclosehook, - addinfo, - addinfourl) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/robotparser.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/robotparser.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/urllib/robotparser.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/urllib/robotparser.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from urllib.robotparser import * -else: - __future_module__ = True - from robotparser import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/winreg.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/winreg.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/winreg.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/winreg.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from winreg import * -else: - __future_module__ = True - from _winreg import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/xmlrpc/client.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/xmlrpc/client.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/xmlrpc/client.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/xmlrpc/client.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from xmlrpc.client import * -else: - from xmlrpclib import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/xmlrpc/server.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/xmlrpc/server.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/moves/xmlrpc/server.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/moves/xmlrpc/server.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -from __future__ import absolute_import -from future.utils import PY3 - -if PY3: - from xmlrpc.server import * -else: - from xmlrpclib import * diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/standard_library/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/standard_library/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/standard_library/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/standard_library/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,814 +0,0 @@ -""" -Python 3 reorganized the standard library (PEP 3108). This module exposes -several standard library modules to Python 2 under their new Python 3 -names. - -It is designed to be used as follows:: - - from future import standard_library - standard_library.install_aliases() - -And then these normal Py3 imports work on both Py3 and Py2:: - - import builtins - import copyreg - import queue - import reprlib - import socketserver - import winreg # on Windows only - import test.support - import html, html.parser, html.entites - import http, http.client, http.server - import http.cookies, http.cookiejar - import urllib.parse, urllib.request, urllib.response, urllib.error, urllib.robotparser - import xmlrpc.client, xmlrpc.server - - import _thread - import _dummy_thread - import _markupbase - - from itertools import filterfalse, zip_longest - from sys import intern - from collections import UserDict, UserList, UserString - from collections import OrderedDict, Counter, ChainMap # even on Py2.6 - from subprocess import getoutput, getstatusoutput - from subprocess import check_output # even on Py2.6 - -(The renamed modules and functions are still available under their old -names on Python 2.) - -This is a cleaner alternative to this idiom (see -http://docs.pythonsprints.com/python3_porting/py-porting.html):: - - try: - import queue - except ImportError: - import Queue as queue - - -Limitations ------------ -We don't currently support these modules, but would like to:: - - import dbm - import dbm.dumb - import dbm.gnu - import collections.abc # on Py33 - import pickle # should (optionally) bring in cPickle on Python 2 - -""" - -from __future__ import absolute_import, division, print_function - -import sys -import logging -import imp -import contextlib -import types -import copy -import os - -# Make a dedicated logger; leave the root logger to be configured -# by the application. -flog = logging.getLogger('future_stdlib') -_formatter = logging.Formatter(logging.BASIC_FORMAT) -_handler = logging.StreamHandler() -_handler.setFormatter(_formatter) -flog.addHandler(_handler) -flog.setLevel(logging.WARN) - -from future.utils import PY2, PY3 - -# The modules that are defined under the same names on Py3 but with -# different contents in a significant way (e.g. submodules) are: -# pickle (fast one) -# dbm -# urllib -# test -# email - -REPLACED_MODULES = set(['test', 'urllib', 'pickle', 'dbm']) # add email and dbm when we support it - -# The following module names are not present in Python 2.x, so they cause no -# potential clashes between the old and new names: -# http -# html -# tkinter -# xmlrpc -# Keys: Py2 / real module names -# Values: Py3 / simulated module names -RENAMES = { - # 'cStringIO': 'io', # there's a new io module in Python 2.6 - # that provides StringIO and BytesIO - # 'StringIO': 'io', # ditto - # 'cPickle': 'pickle', - '__builtin__': 'builtins', - 'copy_reg': 'copyreg', - 'Queue': 'queue', - 'future.moves.socketserver': 'socketserver', - 'ConfigParser': 'configparser', - 'repr': 'reprlib', - # 'FileDialog': 'tkinter.filedialog', - # 'tkFileDialog': 'tkinter.filedialog', - # 'SimpleDialog': 'tkinter.simpledialog', - # 'tkSimpleDialog': 'tkinter.simpledialog', - # 'tkColorChooser': 'tkinter.colorchooser', - # 'tkCommonDialog': 'tkinter.commondialog', - # 'Dialog': 'tkinter.dialog', - # 'Tkdnd': 'tkinter.dnd', - # 'tkFont': 'tkinter.font', - # 'tkMessageBox': 'tkinter.messagebox', - # 'ScrolledText': 'tkinter.scrolledtext', - # 'Tkconstants': 'tkinter.constants', - # 'Tix': 'tkinter.tix', - # 'ttk': 'tkinter.ttk', - # 'Tkinter': 'tkinter', - '_winreg': 'winreg', - 'thread': '_thread', - 'dummy_thread': '_dummy_thread', - # 'anydbm': 'dbm', # causes infinite import loop - # 'whichdb': 'dbm', # causes infinite import loop - # anydbm and whichdb are handled by fix_imports2 - # 'dbhash': 'dbm.bsd', - # 'dumbdbm': 'dbm.dumb', - # 'dbm': 'dbm.ndbm', - # 'gdbm': 'dbm.gnu', - 'future.moves.xmlrpc': 'xmlrpc', - # 'future.backports.email': 'email', # for use by urllib - # 'DocXMLRPCServer': 'xmlrpc.server', - # 'SimpleXMLRPCServer': 'xmlrpc.server', - # 'httplib': 'http.client', - # 'htmlentitydefs' : 'html.entities', - # 'HTMLParser' : 'html.parser', - # 'Cookie': 'http.cookies', - # 'cookielib': 'http.cookiejar', - # 'BaseHTTPServer': 'http.server', - # 'SimpleHTTPServer': 'http.server', - # 'CGIHTTPServer': 'http.server', - # 'future.backports.test': 'test', # primarily for renaming test_support to support - # 'commands': 'subprocess', - # 'urlparse' : 'urllib.parse', - # 'robotparser' : 'urllib.robotparser', - # 'abc': 'collections.abc', # for Py33 - # 'future.utils.six.moves.html': 'html', - # 'future.utils.six.moves.http': 'http', - 'future.moves.html': 'html', - 'future.moves.http': 'http', - # 'future.backports.urllib': 'urllib', - # 'future.utils.six.moves.urllib': 'urllib', - 'future.moves._markupbase': '_markupbase', - } - - -# It is complicated and apparently brittle to mess around with the -# ``sys.modules`` cache in order to support "import urllib" meaning two -# different things (Py2.7 urllib and backported Py3.3-like urllib) in different -# contexts. So we require explicit imports for these modules. -assert len(set(RENAMES.values()) & set(REPLACED_MODULES)) == 0 - - -# Harmless renames that we can insert. -# These modules need names from elsewhere being added to them: -# subprocess: should provide getoutput and other fns from commands -# module but these fns are missing: getstatus, mk2arg, -# mkarg -# re: needs an ASCII constant that works compatibly with Py3 - -# etc: see lib2to3/fixes/fix_imports.py - -# (New module name, new object name, old module name, old object name) -MOVES = [('collections', 'UserList', 'UserList', 'UserList'), - ('collections', 'UserDict', 'UserDict', 'UserDict'), - ('collections', 'UserString','UserString', 'UserString'), - ('itertools', 'filterfalse','itertools', 'ifilterfalse'), - ('itertools', 'zip_longest','itertools', 'izip_longest'), - ('sys', 'intern','__builtin__', 'intern'), - # The re module has no ASCII flag in Py2, but this is the default. - # Set re.ASCII to a zero constant. stat.ST_MODE just happens to be one - # (and it exists on Py2.6+). - ('re', 'ASCII','stat', 'ST_MODE'), - ('base64', 'encodebytes','base64', 'encodestring'), - ('base64', 'decodebytes','base64', 'decodestring'), - ('subprocess', 'getoutput', 'commands', 'getoutput'), - ('subprocess', 'getstatusoutput', 'commands', 'getstatusoutput'), - ('subprocess', 'check_output', 'future.backports.misc', 'check_output'), - ('math', 'ceil', 'future.backports.misc', 'ceil'), - ('collections', 'OrderedDict', 'future.backports.misc', 'OrderedDict'), - ('collections', 'Counter', 'future.backports.misc', 'Counter'), - ('collections', 'ChainMap', 'future.backports.misc', 'ChainMap'), - ('itertools', 'count', 'future.backports.misc', 'count'), - ('reprlib', 'recursive_repr', 'future.backports.misc', 'recursive_repr'), - ('functools', 'cmp_to_key', 'future.backports.misc', 'cmp_to_key'), - -# This is no use, since "import urllib.request" etc. still fails: -# ('urllib', 'error', 'future.moves.urllib', 'error'), -# ('urllib', 'parse', 'future.moves.urllib', 'parse'), -# ('urllib', 'request', 'future.moves.urllib', 'request'), -# ('urllib', 'response', 'future.moves.urllib', 'response'), -# ('urllib', 'robotparser', 'future.moves.urllib', 'robotparser'), - ] - - -# A minimal example of an import hook: -# class WarnOnImport(object): -# def __init__(self, *args): -# self.module_names = args -# -# def find_module(self, fullname, path=None): -# if fullname in self.module_names: -# self.path = path -# return self -# return None -# -# def load_module(self, name): -# if name in sys.modules: -# return sys.modules[name] -# module_info = imp.find_module(name, self.path) -# module = imp.load_module(name, *module_info) -# sys.modules[name] = module -# flog.warning("Imported deprecated module %s", name) -# return module - - -class RenameImport(object): - """ - A class for import hooks mapping Py3 module names etc. to the Py2 equivalents. - """ - # Different RenameImport classes are created when importing this module from - # different source files. This causes isinstance(hook, RenameImport) checks - # to produce inconsistent results. We add this RENAMER attribute here so - # remove_hooks() and install_hooks() can find instances of these classes - # easily: - RENAMER = True - - def __init__(self, old_to_new): - ''' - Pass in a dictionary-like object mapping from old names to new - names. E.g. {'ConfigParser': 'configparser', 'cPickle': 'pickle'} - ''' - self.old_to_new = old_to_new - both = set(old_to_new.keys()) & set(old_to_new.values()) - assert (len(both) == 0 and - len(set(old_to_new.values())) == len(old_to_new.values())), \ - 'Ambiguity in renaming (handler not implemented)' - self.new_to_old = dict((new, old) for (old, new) in old_to_new.items()) - - def find_module(self, fullname, path=None): - # Handles hierarchical importing: package.module.module2 - new_base_names = set([s.split('.')[0] for s in self.new_to_old]) - # Before v0.12: Was: if fullname in set(self.old_to_new) | new_base_names: - if fullname in new_base_names: - return self - return None - - def load_module(self, name): - path = None - if name in sys.modules: - return sys.modules[name] - elif name in self.new_to_old: - # New name. Look up the corresponding old (Py2) name: - oldname = self.new_to_old[name] - module = self._find_and_load_module(oldname) - # module.__future_module__ = True - else: - module = self._find_and_load_module(name) - # In any case, make it available under the requested (Py3) name - sys.modules[name] = module - return module - - def _find_and_load_module(self, name, path=None): - """ - Finds and loads it. But if there's a . in the name, handles it - properly. - """ - bits = name.split('.') - while len(bits) > 1: - # Treat the first bit as a package - packagename = bits.pop(0) - package = self._find_and_load_module(packagename, path) - try: - path = package.__path__ - except AttributeError: - # This could be e.g. moves. - flog.debug('Package {0} has no __path__.'.format(package)) - if name in sys.modules: - return sys.modules[name] - flog.debug('What to do here?') - - name = bits[0] - module_info = imp.find_module(name, path) - return imp.load_module(name, *module_info) - - -class hooks(object): - """ - Acts as a context manager. Saves the state of sys.modules and restores it - after the 'with' block. - - Use like this: - - >>> from future import standard_library - >>> with standard_library.hooks(): - ... import http.client - >>> import requests - - For this to work, http.client will be scrubbed from sys.modules after the - 'with' block. That way the modules imported in the 'with' block will - continue to be accessible in the current namespace but not from any - imported modules (like requests). - """ - def __enter__(self): - # flog.debug('Entering hooks context manager') - self.old_sys_modules = copy.copy(sys.modules) - self.hooks_were_installed = detect_hooks() - # self.scrubbed = scrub_py2_sys_modules() - install_hooks() - return self - - def __exit__(self, *args): - # flog.debug('Exiting hooks context manager') - # restore_sys_modules(self.scrubbed) - if not self.hooks_were_installed: - remove_hooks() - # scrub_future_sys_modules() - -# Sanity check for is_py2_stdlib_module(): We aren't replacing any -# builtin modules names: -if PY2: - assert len(set(RENAMES.values()) & set(sys.builtin_module_names)) == 0 - - -def is_py2_stdlib_module(m): - """ - Tries to infer whether the module m is from the Python 2 standard library. - This may not be reliable on all systems. - """ - if PY3: - return False - if not 'stdlib_path' in is_py2_stdlib_module.__dict__: - stdlib_files = [contextlib.__file__, os.__file__, copy.__file__] - stdlib_paths = [os.path.split(f)[0] for f in stdlib_files] - if not len(set(stdlib_paths)) == 1: - # This seems to happen on travis-ci.org. Very strange. We'll try to - # ignore it. - flog.warn('Multiple locations found for the Python standard ' - 'library: %s' % stdlib_paths) - # Choose the first one arbitrarily - is_py2_stdlib_module.stdlib_path = stdlib_paths[0] - - if m.__name__ in sys.builtin_module_names: - return True - - if hasattr(m, '__file__'): - modpath = os.path.split(m.__file__) - if (modpath[0].startswith(is_py2_stdlib_module.stdlib_path) and - 'site-packages' not in modpath[0]): - return True - - return False - - -def scrub_py2_sys_modules(): - """ - Removes any Python 2 standard library modules from ``sys.modules`` that - would interfere with Py3-style imports using import hooks. Examples are - modules with the same names (like urllib or email). - - (Note that currently import hooks are disabled for modules like these - with ambiguous names anyway ...) - """ - if PY3: - return {} - scrubbed = {} - for modulename in REPLACED_MODULES & set(RENAMES.keys()): - if not modulename in sys.modules: - continue - - module = sys.modules[modulename] - - if is_py2_stdlib_module(module): - flog.debug('Deleting (Py2) {} from sys.modules'.format(modulename)) - scrubbed[modulename] = sys.modules[modulename] - del sys.modules[modulename] - return scrubbed - - -def scrub_future_sys_modules(): - """ - Deprecated. - """ - return {} - -class suspend_hooks(object): - """ - Acts as a context manager. Use like this: - - >>> from future import standard_library - >>> standard_library.install_hooks() - >>> import http.client - >>> # ... - >>> with standard_library.suspend_hooks(): - >>> import requests # incompatible with ``future``'s standard library hooks - - If the hooks were disabled before the context, they are not installed when - the context is left. - """ - def __enter__(self): - self.hooks_were_installed = detect_hooks() - remove_hooks() - # self.scrubbed = scrub_future_sys_modules() - return self - - def __exit__(self, *args): - if self.hooks_were_installed: - install_hooks() - # restore_sys_modules(self.scrubbed) - - -def restore_sys_modules(scrubbed): - """ - Add any previously scrubbed modules back to the sys.modules cache, - but only if it's safe to do so. - """ - clash = set(sys.modules) & set(scrubbed) - if len(clash) != 0: - # If several, choose one arbitrarily to raise an exception about - first = list(clash)[0] - raise ImportError('future module {} clashes with Py2 module' - .format(first)) - sys.modules.update(scrubbed) - - -def install_aliases(): - """ - Monkey-patches the standard library in Py2.6/7 to provide - aliases for better Py3 compatibility. - """ - if PY3: - return - # if hasattr(install_aliases, 'run_already'): - # return - for (newmodname, newobjname, oldmodname, oldobjname) in MOVES: - __import__(newmodname) - # We look up the module in sys.modules because __import__ just returns the - # top-level package: - newmod = sys.modules[newmodname] - # newmod.__future_module__ = True - - __import__(oldmodname) - oldmod = sys.modules[oldmodname] - - obj = getattr(oldmod, oldobjname) - setattr(newmod, newobjname, obj) - - # Hack for urllib so it appears to have the same structure on Py2 as on Py3 - import urllib - from future.backports.urllib import request - from future.backports.urllib import response - from future.backports.urllib import parse - from future.backports.urllib import error - from future.backports.urllib import robotparser - urllib.request = request - urllib.response = response - urllib.parse = parse - urllib.error = error - urllib.robotparser = robotparser - sys.modules['urllib.request'] = request - sys.modules['urllib.response'] = response - sys.modules['urllib.parse'] = parse - sys.modules['urllib.error'] = error - sys.modules['urllib.robotparser'] = robotparser - - # Patch the test module so it appears to have the same structure on Py2 as on Py3 - try: - import test - except ImportError: - pass - try: - from future.moves.test import support - except ImportError: - pass - else: - test.support = support - sys.modules['test.support'] = support - - # Patch the dbm module so it appears to have the same structure on Py2 as on Py3 - try: - import dbm - except ImportError: - pass - else: - from future.moves.dbm import dumb - dbm.dumb = dumb - sys.modules['dbm.dumb'] = dumb - try: - from future.moves.dbm import gnu - except ImportError: - pass - else: - dbm.gnu = gnu - sys.modules['dbm.gnu'] = gnu - try: - from future.moves.dbm import ndbm - except ImportError: - pass - else: - dbm.ndbm = ndbm - sys.modules['dbm.ndbm'] = ndbm - - # install_aliases.run_already = True - - -def install_hooks(): - """ - This function installs the future.standard_library import hook into - sys.meta_path. - """ - if PY3: - return - - install_aliases() - - flog.debug('sys.meta_path was: {0}'.format(sys.meta_path)) - flog.debug('Installing hooks ...') - - # Add it unless it's there already - newhook = RenameImport(RENAMES) - if not detect_hooks(): - sys.meta_path.append(newhook) - flog.debug('sys.meta_path is now: {0}'.format(sys.meta_path)) - - -def enable_hooks(): - """ - Deprecated. Use install_hooks() instead. This will be removed by - ``future`` v1.0. - """ - install_hooks() - - -def remove_hooks(scrub_sys_modules=False): - """ - This function removes the import hook from sys.meta_path. - """ - if PY3: - return - flog.debug('Uninstalling hooks ...') - # Loop backwards, so deleting items keeps the ordering: - for i, hook in list(enumerate(sys.meta_path))[::-1]: - if hasattr(hook, 'RENAMER'): - del sys.meta_path[i] - - # Explicit is better than implicit. In the future the interface should - # probably change so that scrubbing the import hooks requires a separate - # function call. Left as is for now for backward compatibility with - # v0.11.x. - if scrub_sys_modules: - scrub_future_sys_modules() - - -def disable_hooks(): - """ - Deprecated. Use remove_hooks() instead. This will be removed by - ``future`` v1.0. - """ - remove_hooks() - - -def detect_hooks(): - """ - Returns True if the import hooks are installed, False if not. - """ - flog.debug('Detecting hooks ...') - present = any([hasattr(hook, 'RENAMER') for hook in sys.meta_path]) - if present: - flog.debug('Detected.') - else: - flog.debug('Not detected.') - return present - - -# As of v0.12, this no longer happens implicitly: -# if not PY3: -# install_hooks() - - -if not hasattr(sys, 'py2_modules'): - sys.py2_modules = {} - -def cache_py2_modules(): - """ - Currently this function is unneeded, as we are not attempting to provide import hooks - for modules with ambiguous names: email, urllib, pickle. - """ - if len(sys.py2_modules) != 0: - return - assert not detect_hooks() - import urllib - sys.py2_modules['urllib'] = urllib - - import email - sys.py2_modules['email'] = email - - import pickle - sys.py2_modules['pickle'] = pickle - - # Not all Python installations have test module. (Anaconda doesn't, for example.) - # try: - # import test - # except ImportError: - # sys.py2_modules['test'] = None - # sys.py2_modules['test'] = test - - # import dbm - # sys.py2_modules['dbm'] = dbm - - -def import_(module_name, backport=False): - """ - Pass a (potentially dotted) module name of a Python 3 standard library - module. This function imports the module compatibly on Py2 and Py3 and - returns the top-level module. - - Example use: - >>> http = import_('http.client') - >>> http = import_('http.server') - >>> urllib = import_('urllib.request') - - Then: - >>> conn = http.client.HTTPConnection(...) - >>> response = urllib.request.urlopen('http://mywebsite.com') - >>> # etc. - - Use as follows: - >>> package_name = import_(module_name) - - On Py3, equivalent to this: - - >>> import module_name - - On Py2, equivalent to this if backport=False: - - >>> from future.moves import module_name - - or to this if backport=True: - - >>> from future.backports import module_name - - except that it also handles dotted module names such as ``http.client`` - The effect then is like this: - - >>> from future.backports import module - >>> from future.backports.module import submodule - >>> module.submodule = submodule - - Note that this would be a SyntaxError in Python: - - >>> from future.backports import http.client - - """ - # Python 2.6 doesn't have importlib in the stdlib, so it requires - # the backported ``importlib`` package from PyPI as a dependency to use - # this function: - import importlib - - if PY3: - return __import__(module_name) - else: - # client.blah = blah - # Then http.client = client - # etc. - if backport: - prefix = 'future.backports' - else: - prefix = 'future.moves' - parts = prefix.split('.') + module_name.split('.') - - modules = [] - for i, part in enumerate(parts): - sofar = '.'.join(parts[:i+1]) - modules.append(importlib.import_module(sofar)) - for i, part in reversed(list(enumerate(parts))): - if i == 0: - break - setattr(modules[i-1], part, modules[i]) - - # Return the next-most top-level module after future.backports / future.moves: - return modules[2] - - -def from_import(module_name, *symbol_names, **kwargs): - """ - Example use: - >>> HTTPConnection = from_import('http.client', 'HTTPConnection') - >>> HTTPServer = from_import('http.server', 'HTTPServer') - >>> urlopen, urlparse = from_import('urllib.request', 'urlopen', 'urlparse') - - Equivalent to this on Py3: - - >>> from module_name import symbol_names[0], symbol_names[1], ... - - and this on Py2: - - >>> from future.moves.module_name import symbol_names[0], ... - - or: - - >>> from future.backports.module_name import symbol_names[0], ... - - except that it also handles dotted module names such as ``http.client``. - """ - - if PY3: - return __import__(module_name) - else: - if 'backport' in kwargs and bool(kwargs['backport']): - prefix = 'future.backports' - else: - prefix = 'future.moves' - parts = prefix.split('.') + module_name.split('.') - module = importlib.import_module(prefix + '.' + module_name) - output = [getattr(module, name) for name in symbol_names] - if len(output) == 1: - return output[0] - else: - return output - - -class exclude_local_folder_imports(object): - """ - A context-manager that prevents standard library modules like configparser - from being imported from the local python-future source folder on Py3. - - (This was need prior to v0.16.0 because the presence of a configparser - folder would otherwise have prevented setuptools from running on Py3. Maybe - it's not needed any more?) - """ - def __init__(self, *args): - assert len(args) > 0 - self.module_names = args - # Disallow dotted module names like http.client: - if any(['.' in m for m in self.module_names]): - raise NotImplementedError('Dotted module names are not supported') - - def __enter__(self): - self.old_sys_path = copy.copy(sys.path) - self.old_sys_modules = copy.copy(sys.modules) - if sys.version_info[0] < 3: - return - # The presence of all these indicates we've found our source folder, - # because `builtins` won't have been installed in site-packages by setup.py: - FUTURE_SOURCE_SUBFOLDERS = ['future', 'past', 'libfuturize', 'libpasteurize', 'builtins'] - - # Look for the future source folder: - for folder in self.old_sys_path: - if all([os.path.exists(os.path.join(folder, subfolder)) - for subfolder in FUTURE_SOURCE_SUBFOLDERS]): - # Found it. Remove it. - sys.path.remove(folder) - - # Ensure we import the system module: - for m in self.module_names: - # Delete the module and any submodules from sys.modules: - # for key in list(sys.modules): - # if key == m or key.startswith(m + '.'): - # try: - # del sys.modules[key] - # except KeyError: - # pass - try: - module = __import__(m, level=0) - except ImportError: - # There's a problem importing the system module. E.g. the - # winreg module is not available except on Windows. - pass - - def __exit__(self, *args): - # Restore sys.path and sys.modules: - sys.path = self.old_sys_path - for m in set(self.old_sys_modules.keys()) - set(sys.modules.keys()): - sys.modules[m] = self.old_sys_modules[m] - -TOP_LEVEL_MODULES = ['builtins', - 'copyreg', - 'html', - 'http', - 'queue', - 'reprlib', - 'socketserver', - 'test', - 'tkinter', - 'winreg', - 'xmlrpc', - '_dummy_thread', - '_markupbase', - '_thread', - ] - -def import_top_level_modules(): - with exclude_local_folder_imports(*TOP_LEVEL_MODULES): - for m in TOP_LEVEL_MODULES: - try: - __import__(m) - except ImportError: # e.g. winreg - pass diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/tests/base.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/tests/base.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/tests/base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/tests/base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,531 +0,0 @@ -from __future__ import print_function, absolute_import -import os -import tempfile -import unittest -import sys -import re -import warnings -import io -from textwrap import dedent - -from future.utils import bind_method, PY26, PY3, PY2, PY27 -from future.moves.subprocess import check_output, STDOUT, CalledProcessError - -if PY26: - import unittest2 as unittest - - -def reformat_code(code): - """ - Removes any leading \n and dedents. - """ - if code.startswith('\n'): - code = code[1:] - return dedent(code) - - -def order_future_lines(code): - """ - Returns the code block with any ``__future__`` import lines sorted, and - then any ``future`` import lines sorted, then any ``builtins`` import lines - sorted. - - This only sorts the lines within the expected blocks. - - See test_order_future_lines() for an example. - """ - - # We need .splitlines(keepends=True), which doesn't exist on Py2, - # so we use this instead: - lines = code.split('\n') - - uufuture_line_numbers = [i for i, line in enumerate(lines) - if line.startswith('from __future__ import ')] - - future_line_numbers = [i for i, line in enumerate(lines) - if line.startswith('from future') - or line.startswith('from past')] - - builtins_line_numbers = [i for i, line in enumerate(lines) - if line.startswith('from builtins')] - - assert code.lstrip() == code, ('internal usage error: ' - 'dedent the code before calling order_future_lines()') - - def mymax(numbers): - return max(numbers) if len(numbers) > 0 else 0 - - def mymin(numbers): - return min(numbers) if len(numbers) > 0 else float('inf') - - assert mymax(uufuture_line_numbers) <= mymin(future_line_numbers), \ - 'the __future__ and future imports are out of order' - - # assert mymax(future_line_numbers) <= mymin(builtins_line_numbers), \ - # 'the future and builtins imports are out of order' - - uul = sorted([lines[i] for i in uufuture_line_numbers]) - sorted_uufuture_lines = dict(zip(uufuture_line_numbers, uul)) - - fl = sorted([lines[i] for i in future_line_numbers]) - sorted_future_lines = dict(zip(future_line_numbers, fl)) - - bl = sorted([lines[i] for i in builtins_line_numbers]) - sorted_builtins_lines = dict(zip(builtins_line_numbers, bl)) - - # Replace the old unsorted "from __future__ import ..." lines with the - # new sorted ones: - new_lines = [] - for i in range(len(lines)): - if i in uufuture_line_numbers: - new_lines.append(sorted_uufuture_lines[i]) - elif i in future_line_numbers: - new_lines.append(sorted_future_lines[i]) - elif i in builtins_line_numbers: - new_lines.append(sorted_builtins_lines[i]) - else: - new_lines.append(lines[i]) - return '\n'.join(new_lines) - - -class VerboseCalledProcessError(CalledProcessError): - """ - Like CalledProcessError, but it displays more information (message and - script output) for diagnosing test failures etc. - """ - def __init__(self, msg, returncode, cmd, output=None): - self.msg = msg - self.returncode = returncode - self.cmd = cmd - self.output = output - - def __str__(self): - return ("Command '%s' failed with exit status %d\nMessage: %s\nOutput: %s" - % (self.cmd, self.returncode, self.msg, self.output)) - -class FuturizeError(VerboseCalledProcessError): - pass - -class PasteurizeError(VerboseCalledProcessError): - pass - - -class CodeHandler(unittest.TestCase): - """ - Handy mixin for test classes for writing / reading / futurizing / - running .py files in the test suite. - """ - def setUp(self): - """ - The outputs from the various futurize stages should have the - following headers: - """ - # After stage1: - # TODO: use this form after implementing a fixer to consolidate - # __future__ imports into a single line: - # self.headers1 = """ - # from __future__ import absolute_import, division, print_function - # """ - self.headers1 = reformat_code(""" - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - """) - - # After stage2 --all-imports: - # TODO: use this form after implementing a fixer to consolidate - # __future__ imports into a single line: - # self.headers2 = """ - # from __future__ import (absolute_import, division, - # print_function, unicode_literals) - # from future import standard_library - # from future.builtins import * - # """ - self.headers2 = reformat_code(""" - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - from __future__ import unicode_literals - from future import standard_library - standard_library.install_aliases() - from builtins import * - """) - self.interpreters = [sys.executable] - self.tempdir = tempfile.mkdtemp() + os.path.sep - pypath = os.getenv('PYTHONPATH') - if pypath: - self.env = {'PYTHONPATH': os.getcwd() + os.pathsep + pypath} - else: - self.env = {'PYTHONPATH': os.getcwd()} - - def convert(self, code, stages=(1, 2), all_imports=False, from3=False, - reformat=True, run=True, conservative=False): - """ - Converts the code block using ``futurize`` and returns the - resulting code. - - Passing stages=[1] or stages=[2] passes the flag ``--stage1`` or - ``stage2`` to ``futurize``. Passing both stages runs ``futurize`` - with both stages by default. - - If from3 is False, runs ``futurize``, converting from Python 2 to - both 2 and 3. If from3 is True, runs ``pasteurize`` to convert - from Python 3 to both 2 and 3. - - Optionally reformats the code block first using the reformat() function. - - If run is True, runs the resulting code under all Python - interpreters in self.interpreters. - """ - if reformat: - code = reformat_code(code) - self._write_test_script(code) - self._futurize_test_script(stages=stages, all_imports=all_imports, - from3=from3, conservative=conservative) - output = self._read_test_script() - if run: - for interpreter in self.interpreters: - _ = self._run_test_script(interpreter=interpreter) - return output - - def compare(self, output, expected, ignore_imports=True): - """ - Compares whether the code blocks are equal. If not, raises an - exception so the test fails. Ignores any trailing whitespace like - blank lines. - - If ignore_imports is True, passes the code blocks into the - strip_future_imports method. - - If one code block is a unicode string and the other a - byte-string, it assumes the byte-string is encoded as utf-8. - """ - if ignore_imports: - output = self.strip_future_imports(output) - expected = self.strip_future_imports(expected) - if isinstance(output, bytes) and not isinstance(expected, bytes): - output = output.decode('utf-8') - if isinstance(expected, bytes) and not isinstance(output, bytes): - expected = expected.decode('utf-8') - self.assertEqual(order_future_lines(output.rstrip()), - expected.rstrip()) - - def strip_future_imports(self, code): - """ - Strips any of these import lines: - - from __future__ import - from future - from future. - from builtins - - or any line containing: - install_hooks() - or: - install_aliases() - - Limitation: doesn't handle imports split across multiple lines like - this: - - from __future__ import (absolute_import, division, print_function, - unicode_literals) - """ - output = [] - # We need .splitlines(keepends=True), which doesn't exist on Py2, - # so we use this instead: - for line in code.split('\n'): - if not (line.startswith('from __future__ import ') - or line.startswith('from future ') - or line.startswith('from builtins ') - or 'install_hooks()' in line - or 'install_aliases()' in line - # but don't match "from future_builtins" :) - or line.startswith('from future.')): - output.append(line) - return '\n'.join(output) - - def convert_check(self, before, expected, stages=(1, 2), all_imports=False, - ignore_imports=True, from3=False, run=True, - conservative=False): - """ - Convenience method that calls convert() and compare(). - - Reformats the code blocks automatically using the reformat_code() - function. - - If all_imports is passed, we add the appropriate import headers - for the stage(s) selected to the ``expected`` code-block, so they - needn't appear repeatedly in the test code. - - If ignore_imports is True, ignores the presence of any lines - beginning: - - from __future__ import ... - from future import ... - - for the purpose of the comparison. - """ - output = self.convert(before, stages=stages, all_imports=all_imports, - from3=from3, run=run, conservative=conservative) - if all_imports: - headers = self.headers2 if 2 in stages else self.headers1 - else: - headers = '' - - self.compare(output, headers + reformat_code(expected), - ignore_imports=ignore_imports) - - def unchanged(self, code, **kwargs): - """ - Convenience method to ensure the code is unchanged by the - futurize process. - """ - self.convert_check(code, code, **kwargs) - - def _write_test_script(self, code, filename='mytestscript.py'): - """ - Dedents the given code (a multiline string) and writes it out to - a file in a temporary folder like /tmp/tmpUDCn7x/mytestscript.py. - """ - if isinstance(code, bytes): - code = code.decode('utf-8') - # Be explicit about encoding the temp file as UTF-8 (issue #63): - with io.open(self.tempdir + filename, 'wt', encoding='utf-8') as f: - f.write(dedent(code)) - - def _read_test_script(self, filename='mytestscript.py'): - with io.open(self.tempdir + filename, 'rt', encoding='utf-8') as f: - newsource = f.read() - return newsource - - def _futurize_test_script(self, filename='mytestscript.py', stages=(1, 2), - all_imports=False, from3=False, - conservative=False): - params = [] - stages = list(stages) - if all_imports: - params.append('--all-imports') - if from3: - script = 'pasteurize.py' - else: - script = 'futurize.py' - if stages == [1]: - params.append('--stage1') - elif stages == [2]: - params.append('--stage2') - else: - assert stages == [1, 2] - if conservative: - params.append('--conservative') - # No extra params needed - - # Absolute file path: - fn = self.tempdir + filename - call_args = [sys.executable, script] + params + ['-w', fn] - try: - output = check_output(call_args, stderr=STDOUT, env=self.env) - except CalledProcessError as e: - with open(fn) as f: - msg = ( - 'Error running the command %s\n' - '%s\n' - 'Contents of file %s:\n' - '\n' - '%s') % ( - ' '.join(call_args), - 'env=%s' % self.env, - fn, - '----\n%s\n----' % f.read(), - ) - ErrorClass = (FuturizeError if 'futurize' in script else PasteurizeError) - raise ErrorClass(msg, e.returncode, e.cmd, output=e.output) - return output - - def _run_test_script(self, filename='mytestscript.py', - interpreter=sys.executable): - # Absolute file path: - fn = self.tempdir + filename - try: - output = check_output([interpreter, fn], - env=self.env, stderr=STDOUT) - except CalledProcessError as e: - with open(fn) as f: - msg = ( - 'Error running the command %s\n' - '%s\n' - 'Contents of file %s:\n' - '\n' - '%s') % ( - ' '.join([interpreter, fn]), - 'env=%s' % self.env, - fn, - '----\n%s\n----' % f.read(), - ) - if not hasattr(e, 'output'): - # The attribute CalledProcessError.output doesn't exist on Py2.6 - e.output = None - raise VerboseCalledProcessError(msg, e.returncode, e.cmd, output=e.output) - return output - - -# Decorator to skip some tests on Python 2.6 ... -skip26 = unittest.skipIf(PY26, "this test is known to fail on Py2.6") - - -def expectedFailurePY3(func): - if not PY3: - return func - return unittest.expectedFailure(func) - -def expectedFailurePY26(func): - if not PY26: - return func - return unittest.expectedFailure(func) - - -def expectedFailurePY27(func): - if not PY27: - return func - return unittest.expectedFailure(func) - - -def expectedFailurePY2(func): - if not PY2: - return func - return unittest.expectedFailure(func) - - -# Renamed in Py3.3: -if not hasattr(unittest.TestCase, 'assertRaisesRegex'): - unittest.TestCase.assertRaisesRegex = unittest.TestCase.assertRaisesRegexp - -# From Py3.3: -def assertRegex(self, text, expected_regex, msg=None): - """Fail the test unless the text matches the regular expression.""" - if isinstance(expected_regex, (str, unicode)): - assert expected_regex, "expected_regex must not be empty." - expected_regex = re.compile(expected_regex) - if not expected_regex.search(text): - msg = msg or "Regex didn't match" - msg = '%s: %r not found in %r' % (msg, expected_regex.pattern, text) - raise self.failureException(msg) - -if not hasattr(unittest.TestCase, 'assertRegex'): - bind_method(unittest.TestCase, 'assertRegex', assertRegex) - -class _AssertRaisesBaseContext(object): - - def __init__(self, expected, test_case, callable_obj=None, - expected_regex=None): - self.expected = expected - self.test_case = test_case - if callable_obj is not None: - try: - self.obj_name = callable_obj.__name__ - except AttributeError: - self.obj_name = str(callable_obj) - else: - self.obj_name = None - if isinstance(expected_regex, (bytes, str)): - expected_regex = re.compile(expected_regex) - self.expected_regex = expected_regex - self.msg = None - - def _raiseFailure(self, standardMsg): - msg = self.test_case._formatMessage(self.msg, standardMsg) - raise self.test_case.failureException(msg) - - def handle(self, name, callable_obj, args, kwargs): - """ - If callable_obj is None, assertRaises/Warns is being used as a - context manager, so check for a 'msg' kwarg and return self. - If callable_obj is not None, call it passing args and kwargs. - """ - if callable_obj is None: - self.msg = kwargs.pop('msg', None) - return self - with self: - callable_obj(*args, **kwargs) - -class _AssertWarnsContext(_AssertRaisesBaseContext): - """A context manager used to implement TestCase.assertWarns* methods.""" - - def __enter__(self): - # The __warningregistry__'s need to be in a pristine state for tests - # to work properly. - for v in sys.modules.values(): - if getattr(v, '__warningregistry__', None): - v.__warningregistry__ = {} - self.warnings_manager = warnings.catch_warnings(record=True) - self.warnings = self.warnings_manager.__enter__() - warnings.simplefilter("always", self.expected) - return self - - def __exit__(self, exc_type, exc_value, tb): - self.warnings_manager.__exit__(exc_type, exc_value, tb) - if exc_type is not None: - # let unexpected exceptions pass through - return - try: - exc_name = self.expected.__name__ - except AttributeError: - exc_name = str(self.expected) - first_matching = None - for m in self.warnings: - w = m.message - if not isinstance(w, self.expected): - continue - if first_matching is None: - first_matching = w - if (self.expected_regex is not None and - not self.expected_regex.search(str(w))): - continue - # store warning for later retrieval - self.warning = w - self.filename = m.filename - self.lineno = m.lineno - return - # Now we simply try to choose a helpful failure message - if first_matching is not None: - self._raiseFailure('"{}" does not match "{}"'.format( - self.expected_regex.pattern, str(first_matching))) - if self.obj_name: - self._raiseFailure("{} not triggered by {}".format(exc_name, - self.obj_name)) - else: - self._raiseFailure("{} not triggered".format(exc_name)) - - -def assertWarns(self, expected_warning, callable_obj=None, *args, **kwargs): - """Fail unless a warning of class warnClass is triggered - by callable_obj when invoked with arguments args and keyword - arguments kwargs. If a different type of warning is - triggered, it will not be handled: depending on the other - warning filtering rules in effect, it might be silenced, printed - out, or raised as an exception. - - If called with callable_obj omitted or None, will return a - context object used like this:: - - with self.assertWarns(SomeWarning): - do_something() - - An optional keyword argument 'msg' can be provided when assertWarns - is used as a context object. - - The context manager keeps a reference to the first matching - warning as the 'warning' attribute; similarly, the 'filename' - and 'lineno' attributes give you information about the line - of Python code from which the warning was triggered. - This allows you to inspect the warning after the assertion:: - - with self.assertWarns(SomeWarning) as cm: - do_something() - the_warning = cm.warning - self.assertEqual(the_warning.some_attribute, 147) - """ - context = _AssertWarnsContext(expected_warning, self, callable_obj) - return context.handle('assertWarns', callable_obj, args, kwargs) - -if not hasattr(unittest.TestCase, 'assertWarns'): - bind_method(unittest.TestCase, 'assertWarns', assertWarns) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,258 +0,0 @@ -""" -This module contains backports the data types that were significantly changed -in the transition from Python 2 to Python 3. - -- an implementation of Python 3's bytes object (pure Python subclass of - Python 2's builtin 8-bit str type) -- an implementation of Python 3's str object (pure Python subclass of - Python 2's builtin unicode type) -- a backport of the range iterator from Py3 with slicing support - -It is used as follows:: - - from __future__ import division, absolute_import, print_function - from builtins import bytes, dict, int, range, str - -to bring in the new semantics for these functions from Python 3. And -then, for example:: - - b = bytes(b'ABCD') - assert list(b) == [65, 66, 67, 68] - assert repr(b) == "b'ABCD'" - assert [65, 66] in b - - # These raise TypeErrors: - # b + u'EFGH' - # b.split(u'B') - # bytes(b',').join([u'Fred', u'Bill']) - - - s = str(u'ABCD') - - # These raise TypeErrors: - # s.join([b'Fred', b'Bill']) - # s.startswith(b'A') - # b'B' in s - # s.find(b'A') - # s.replace(u'A', b'a') - - # This raises an AttributeError: - # s.decode('utf-8') - - assert repr(s) == 'ABCD' # consistent repr with Py3 (no u prefix) - - - for i in range(10**11)[:10]: - pass - -and:: - - class VerboseList(list): - def append(self, item): - print('Adding an item') - super().append(item) # new simpler super() function - -For more information: ---------------------- - -- future.types.newbytes -- future.types.newdict -- future.types.newint -- future.types.newobject -- future.types.newrange -- future.types.newstr - - -Notes -===== - -range() -------- -``range`` is a custom class that backports the slicing behaviour from -Python 3 (based on the ``xrange`` module by Dan Crosta). See the -``newrange`` module docstring for more details. - - -super() -------- -``super()`` is based on Ryan Kelly's ``magicsuper`` module. See the -``newsuper`` module docstring for more details. - - -round() -------- -Python 3 modifies the behaviour of ``round()`` to use "Banker's Rounding". -See http://stackoverflow.com/a/10825998. See the ``newround`` module -docstring for more details. - -""" - -from __future__ import absolute_import, division, print_function - -import functools -from numbers import Integral - -from future import utils - - -# Some utility functions to enforce strict type-separation of unicode str and -# bytes: -def disallow_types(argnums, disallowed_types): - """ - A decorator that raises a TypeError if any of the given numbered - arguments is of the corresponding given type (e.g. bytes or unicode - string). - - For example: - - @disallow_types([0, 1], [unicode, bytes]) - def f(a, b): - pass - - raises a TypeError when f is called if a unicode object is passed as - `a` or a bytes object is passed as `b`. - - This also skips over keyword arguments, so - - @disallow_types([0, 1], [unicode, bytes]) - def g(a, b=None): - pass - - doesn't raise an exception if g is called with only one argument a, - e.g.: - - g(b'Byte string') - - Example use: - - >>> class newbytes(object): - ... @disallow_types([1], [unicode]) - ... def __add__(self, other): - ... pass - - >>> newbytes('1234') + u'1234' #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - TypeError: can't concat 'bytes' to (unicode) str - """ - - def decorator(function): - - @functools.wraps(function) - def wrapper(*args, **kwargs): - # These imports are just for this decorator, and are defined here - # to prevent circular imports: - from .newbytes import newbytes - from .newint import newint - from .newstr import newstr - - errmsg = "argument can't be {0}" - for (argnum, mytype) in zip(argnums, disallowed_types): - # Handle the case where the type is passed as a string like 'newbytes'. - if isinstance(mytype, str) or isinstance(mytype, bytes): - mytype = locals()[mytype] - - # Only restrict kw args only if they are passed: - if len(args) <= argnum: - break - - # Here we use type() rather than isinstance() because - # __instancecheck__ is being overridden. E.g. - # isinstance(b'abc', newbytes) is True on Py2. - if type(args[argnum]) == mytype: - raise TypeError(errmsg.format(mytype)) - - return function(*args, **kwargs) - return wrapper - return decorator - - -def no(mytype, argnums=(1,)): - """ - A shortcut for the disallow_types decorator that disallows only one type - (in any position in argnums). - - Example use: - - >>> class newstr(object): - ... @no('bytes') - ... def __add__(self, other): - ... pass - - >>> newstr(u'1234') + b'1234' #doctest: +IGNORE_EXCEPTION_DETAIL - Traceback (most recent call last): - ... - TypeError: argument can't be bytes - - The object can also be passed directly, but passing the string helps - to prevent circular import problems. - """ - if isinstance(argnums, Integral): - argnums = (argnums,) - disallowed_types = [mytype] * len(argnums) - return disallow_types(argnums, disallowed_types) - - -def issubset(list1, list2): - """ - Examples: - - >>> issubset([], [65, 66, 67]) - True - >>> issubset([65], [65, 66, 67]) - True - >>> issubset([65, 66], [65, 66, 67]) - True - >>> issubset([65, 67], [65, 66, 67]) - False - """ - n = len(list1) - for startpos in range(len(list2) - n + 1): - if list2[startpos:startpos+n] == list1: - return True - return False - - -if utils.PY3: - import builtins - bytes = builtins.bytes - dict = builtins.dict - int = builtins.int - list = builtins.list - object = builtins.object - range = builtins.range - str = builtins.str - - # The identity mapping - newtypes = {bytes: bytes, - dict: dict, - int: int, - list: list, - object: object, - range: range, - str: str} - - __all__ = ['newtypes'] - -else: - - from .newbytes import newbytes - from .newdict import newdict - from .newint import newint - from .newlist import newlist - from .newrange import newrange - from .newobject import newobject - from .newstr import newstr - - newtypes = {bytes: newbytes, - dict: newdict, - int: newint, - long: newint, - list: newlist, - object: newobject, - range: newrange, - str: newbytes, - unicode: newstr} - - __all__ = ['newbytes', 'newdict', 'newint', 'newlist', 'newrange', 'newstr', 'newtypes'] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newbytes.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newbytes.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newbytes.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newbytes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,433 +0,0 @@ -""" -Pure-Python implementation of a Python 3-like bytes object for Python 2. - -Why do this? Without it, the Python 2 bytes object is a very, very -different beast to the Python 3 bytes object. -""" - -from collections import Iterable -from numbers import Integral -import string -import copy - -from future.utils import istext, isbytes, PY3, with_metaclass -from future.types import no, issubset -from future.types.newobject import newobject - - -_builtin_bytes = bytes - -if PY3: - # We'll probably never use newstr on Py3 anyway... - unicode = str - - -class BaseNewBytes(type): - def __instancecheck__(cls, instance): - if cls == newbytes: - return isinstance(instance, _builtin_bytes) - else: - return issubclass(instance.__class__, cls) - - -def _newchr(x): - if isinstance(x, str): # this happens on pypy - return x.encode('ascii') - else: - return chr(x) - - -class newbytes(with_metaclass(BaseNewBytes, _builtin_bytes)): - """ - A backport of the Python 3 bytes object to Py2 - """ - def __new__(cls, *args, **kwargs): - """ - From the Py3 bytes docstring: - - bytes(iterable_of_ints) -> bytes - bytes(string, encoding[, errors]) -> bytes - bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer - bytes(int) -> bytes object of size given by the parameter initialized with null bytes - bytes() -> empty bytes object - - Construct an immutable array of bytes from: - - an iterable yielding integers in range(256) - - a text string encoded using the specified encoding - - any object implementing the buffer API. - - an integer - """ - - encoding = None - errors = None - - if len(args) == 0: - return super(newbytes, cls).__new__(cls) - elif len(args) >= 2: - args = list(args) - if len(args) == 3: - errors = args.pop() - encoding=args.pop() - # Was: elif isinstance(args[0], newbytes): - # We use type() instead of the above because we're redefining - # this to be True for all unicode string subclasses. Warning: - # This may render newstr un-subclassable. - if type(args[0]) == newbytes: - # Special-case: for consistency with Py3.3, we return the same object - # (with the same id) if a newbytes object is passed into the - # newbytes constructor. - return args[0] - elif isinstance(args[0], _builtin_bytes): - value = args[0] - elif isinstance(args[0], unicode): - try: - if 'encoding' in kwargs: - assert encoding is None - encoding = kwargs['encoding'] - if 'errors' in kwargs: - assert errors is None - errors = kwargs['errors'] - except AssertionError: - raise TypeError('Argument given by name and position') - if encoding is None: - raise TypeError('unicode string argument without an encoding') - ### - # Was: value = args[0].encode(**kwargs) - # Python 2.6 string encode() method doesn't take kwargs: - # Use this instead: - newargs = [encoding] - if errors is not None: - newargs.append(errors) - value = args[0].encode(*newargs) - ### - elif hasattr(args[0], '__bytes__'): - value = args[0].__bytes__() - elif isinstance(args[0], Iterable): - if len(args[0]) == 0: - # This could be an empty list or tuple. Return b'' as on Py3. - value = b'' - else: - # Was: elif len(args[0])>0 and isinstance(args[0][0], Integral): - # # It's a list of integers - # But then we can't index into e.g. frozensets. Try to proceed - # anyway. - try: - value = bytearray([_newchr(x) for x in args[0]]) - except: - raise ValueError('bytes must be in range(0, 256)') - elif isinstance(args[0], Integral): - if args[0] < 0: - raise ValueError('negative count') - value = b'\x00' * args[0] - else: - value = args[0] - if type(value) == newbytes: - # Above we use type(...) rather than isinstance(...) because the - # newbytes metaclass overrides __instancecheck__. - # oldbytes(value) gives the wrong thing on Py2: the same - # result as str(value) on Py3, e.g. "b'abc'". (Issue #193). - # So we handle this case separately: - return copy.copy(value) - else: - return super(newbytes, cls).__new__(cls, value) - - def __repr__(self): - return 'b' + super(newbytes, self).__repr__() - - def __str__(self): - return 'b' + "'{0}'".format(super(newbytes, self).__str__()) - - def __getitem__(self, y): - value = super(newbytes, self).__getitem__(y) - if isinstance(y, Integral): - return ord(value) - else: - return newbytes(value) - - def __getslice__(self, *args): - return self.__getitem__(slice(*args)) - - def __contains__(self, key): - if isinstance(key, int): - newbyteskey = newbytes([key]) - # Don't use isinstance() here because we only want to catch - # newbytes, not Python 2 str: - elif type(key) == newbytes: - newbyteskey = key - else: - newbyteskey = newbytes(key) - return issubset(list(newbyteskey), list(self)) - - @no(unicode) - def __add__(self, other): - return newbytes(super(newbytes, self).__add__(other)) - - @no(unicode) - def __radd__(self, left): - return newbytes(left) + self - - @no(unicode) - def __mul__(self, other): - return newbytes(super(newbytes, self).__mul__(other)) - - @no(unicode) - def __rmul__(self, other): - return newbytes(super(newbytes, self).__rmul__(other)) - - def join(self, iterable_of_bytes): - errmsg = 'sequence item {0}: expected bytes, {1} found' - if isbytes(iterable_of_bytes) or istext(iterable_of_bytes): - raise TypeError(errmsg.format(0, type(iterable_of_bytes))) - for i, item in enumerate(iterable_of_bytes): - if istext(item): - raise TypeError(errmsg.format(i, type(item))) - return newbytes(super(newbytes, self).join(iterable_of_bytes)) - - @classmethod - def fromhex(cls, string): - # Only on Py2: - return cls(string.replace(' ', '').decode('hex')) - - @no(unicode) - def find(self, sub, *args): - return super(newbytes, self).find(sub, *args) - - @no(unicode) - def rfind(self, sub, *args): - return super(newbytes, self).rfind(sub, *args) - - @no(unicode, (1, 2)) - def replace(self, old, new, *args): - return newbytes(super(newbytes, self).replace(old, new, *args)) - - def encode(self, *args): - raise AttributeError("encode method has been disabled in newbytes") - - def decode(self, encoding='utf-8', errors='strict'): - """ - Returns a newstr (i.e. unicode subclass) - - Decode B using the codec registered for encoding. Default encoding - is 'utf-8'. errors may be given to set a different error - handling scheme. Default is 'strict' meaning that encoding errors raise - a UnicodeDecodeError. Other possible values are 'ignore' and 'replace' - as well as any other name registered with codecs.register_error that is - able to handle UnicodeDecodeErrors. - """ - # Py2 str.encode() takes encoding and errors as optional parameter, - # not keyword arguments as in Python 3 str. - - from future.types.newstr import newstr - - if errors == 'surrogateescape': - from future.utils.surrogateescape import register_surrogateescape - register_surrogateescape() - - return newstr(super(newbytes, self).decode(encoding, errors)) - - # This is currently broken: - # # We implement surrogateescape error handling here in addition rather - # # than relying on the custom error handler from - # # future.utils.surrogateescape to be registered globally, even though - # # that is fine in the case of decoding. (But not encoding: see the - # # comments in newstr.encode()``.) - # - # if errors == 'surrogateescape': - # # Decode char by char - # mybytes = [] - # for code in self: - # # Code is an int - # if 0x80 <= code <= 0xFF: - # b = 0xDC00 + code - # elif code <= 0x7F: - # b = _unichr(c).decode(encoding=encoding) - # else: - # # # It may be a bad byte - # # FIXME: What to do in this case? See the Py3 docs / tests. - # # # Try swallowing it. - # # continue - # # print("RAISE!") - # raise NotASurrogateError - # mybytes.append(b) - # return newbytes(mybytes) - # return newbytes(super(newstr, self).decode(encoding, errors)) - - @no(unicode) - def startswith(self, prefix, *args): - return super(newbytes, self).startswith(prefix, *args) - - @no(unicode) - def endswith(self, prefix, *args): - return super(newbytes, self).endswith(prefix, *args) - - @no(unicode) - def split(self, sep=None, maxsplit=-1): - # Py2 str.split() takes maxsplit as an optional parameter, not as a - # keyword argument as in Python 3 bytes. - parts = super(newbytes, self).split(sep, maxsplit) - return [newbytes(part) for part in parts] - - def splitlines(self, keepends=False): - """ - B.splitlines([keepends]) -> list of lines - - Return a list of the lines in B, breaking at line boundaries. - Line breaks are not included in the resulting list unless keepends - is given and true. - """ - # Py2 str.splitlines() takes keepends as an optional parameter, - # not as a keyword argument as in Python 3 bytes. - parts = super(newbytes, self).splitlines(keepends) - return [newbytes(part) for part in parts] - - @no(unicode) - def rsplit(self, sep=None, maxsplit=-1): - # Py2 str.rsplit() takes maxsplit as an optional parameter, not as a - # keyword argument as in Python 3 bytes. - parts = super(newbytes, self).rsplit(sep, maxsplit) - return [newbytes(part) for part in parts] - - @no(unicode) - def partition(self, sep): - parts = super(newbytes, self).partition(sep) - return tuple(newbytes(part) for part in parts) - - @no(unicode) - def rpartition(self, sep): - parts = super(newbytes, self).rpartition(sep) - return tuple(newbytes(part) for part in parts) - - @no(unicode, (1,)) - def rindex(self, sub, *args): - ''' - S.rindex(sub [,start [,end]]) -> int - - Like S.rfind() but raise ValueError when the substring is not found. - ''' - pos = self.rfind(sub, *args) - if pos == -1: - raise ValueError('substring not found') - - @no(unicode) - def index(self, sub, *args): - ''' - Returns index of sub in bytes. - Raises ValueError if byte is not in bytes and TypeError if can't - be converted bytes or its length is not 1. - ''' - if isinstance(sub, int): - if len(args) == 0: - start, end = 0, len(self) - elif len(args) == 1: - start = args[0] - elif len(args) == 2: - start, end = args - else: - raise TypeError('takes at most 3 arguments') - return list(self)[start:end].index(sub) - if not isinstance(sub, bytes): - try: - sub = self.__class__(sub) - except (TypeError, ValueError): - raise TypeError("can't convert sub to bytes") - try: - return super(newbytes, self).index(sub, *args) - except ValueError: - raise ValueError('substring not found') - - def __eq__(self, other): - if isinstance(other, (_builtin_bytes, bytearray)): - return super(newbytes, self).__eq__(other) - else: - return False - - def __ne__(self, other): - if isinstance(other, _builtin_bytes): - return super(newbytes, self).__ne__(other) - else: - return True - - unorderable_err = 'unorderable types: bytes() and {0}' - - def __lt__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__lt__(other) - - def __le__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__le__(other) - - def __gt__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__gt__(other) - - def __ge__(self, other): - if not isbytes(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newbytes, self).__ge__(other) - - def __native__(self): - # We can't just feed a newbytes object into str(), because - # newbytes.__str__() returns e.g. "b'blah'", consistent with Py3 bytes. - return super(newbytes, self).__str__() - - def __getattribute__(self, name): - """ - A trick to cause the ``hasattr`` builtin-fn to return False for - the 'encode' method on Py2. - """ - if name in ['encode', u'encode']: - raise AttributeError("encode method has been disabled in newbytes") - return super(newbytes, self).__getattribute__(name) - - @no(unicode) - def rstrip(self, bytes_to_strip=None): - """ - Strip trailing bytes contained in the argument. - If the argument is omitted, strip trailing ASCII whitespace. - """ - return newbytes(super(newbytes, self).rstrip(bytes_to_strip)) - - @no(unicode) - def strip(self, bytes_to_strip=None): - """ - Strip leading and trailing bytes contained in the argument. - If the argument is omitted, strip trailing ASCII whitespace. - """ - return newbytes(super(newbytes, self).strip(bytes_to_strip)) - - def lower(self): - """ - b.lower() -> copy of b - - Return a copy of b with all ASCII characters converted to lowercase. - """ - return newbytes(super(newbytes, self).lower()) - - @no(unicode) - def upper(self): - """ - b.upper() -> copy of b - - Return a copy of b with all ASCII characters converted to uppercase. - """ - return newbytes(super(newbytes, self).upper()) - - @classmethod - @no(unicode) - def maketrans(cls, frm, to): - """ - B.maketrans(frm, to) -> translation table - - Return a translation table (a bytes object of length 256) suitable - for use in the bytes or bytearray translate method where each byte - in frm is mapped to the byte at the same position in to. - The bytes objects frm and to must be of the same length. - """ - return newbytes(string.maketrans(frm, to)) - - -__all__ = ['newbytes'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newdict.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newdict.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newdict.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newdict.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -""" -A dict subclass for Python 2 that behaves like Python 3's dict - -Example use: - ->>> from builtins import dict ->>> d1 = dict() # instead of {} for an empty dict ->>> d2 = dict(key1='value1', key2='value2') - -The keys, values and items methods now return iterators on Python 2.x -(with set-like behaviour on Python 2.7). - ->>> for d in (d1, d2): -... assert not isinstance(d.keys(), list) -... assert not isinstance(d.values(), list) -... assert not isinstance(d.items(), list) -""" - -import sys - -from future.utils import with_metaclass -from future.types.newobject import newobject - - -_builtin_dict = dict -ver = sys.version_info[:2] - - -class BaseNewDict(type): - def __instancecheck__(cls, instance): - if cls == newdict: - return isinstance(instance, _builtin_dict) - else: - return issubclass(instance.__class__, cls) - - -class newdict(with_metaclass(BaseNewDict, _builtin_dict)): - """ - A backport of the Python 3 dict object to Py2 - """ - def items(self): - """ - On Python 2.7+: - D.items() -> a set-like object providing a view on D's items - On Python 2.6: - D.items() -> an iterator over D's items - """ - if ver == (2, 7): - return self.viewitems() - elif ver == (2, 6): - return self.iteritems() - elif ver >= (3, 0): - return self.items() - - def keys(self): - """ - On Python 2.7+: - D.keys() -> a set-like object providing a view on D's keys - On Python 2.6: - D.keys() -> an iterator over D's keys - """ - if ver == (2, 7): - return self.viewkeys() - elif ver == (2, 6): - return self.iterkeys() - elif ver >= (3, 0): - return self.keys() - - def values(self): - """ - On Python 2.7+: - D.values() -> a set-like object providing a view on D's values - On Python 2.6: - D.values() -> an iterator over D's values - """ - if ver == (2, 7): - return self.viewvalues() - elif ver == (2, 6): - return self.itervalues() - elif ver >= (3, 0): - return self.values() - - def __new__(cls, *args, **kwargs): - """ - dict() -> new empty dictionary - dict(mapping) -> new dictionary initialized from a mapping object's - (key, value) pairs - dict(iterable) -> new dictionary initialized as if via: - d = {} - for k, v in iterable: - d[k] = v - dict(**kwargs) -> new dictionary initialized with the name=value pairs - in the keyword argument list. For example: dict(one=1, two=2) - """ - - if len(args) == 0: - return super(newdict, cls).__new__(cls) - elif type(args[0]) == newdict: - value = args[0] - else: - value = args[0] - return super(newdict, cls).__new__(cls, value) - - def __native__(self): - """ - Hook for the future.utils.native() function - """ - return dict(self) - - -__all__ = ['newdict'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newint.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newint.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newint.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newint.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,379 +0,0 @@ -""" -Backport of Python 3's int, based on Py2's long. - -They are very similar. The most notable difference is: - -- representation: trailing L in Python 2 removed in Python 3 -""" -from __future__ import division - -import struct -import collections - -from future.types.newbytes import newbytes -from future.types.newobject import newobject -from future.utils import PY3, isint, istext, isbytes, with_metaclass, native - - -if PY3: - long = int - - -class BaseNewInt(type): - def __instancecheck__(cls, instance): - if cls == newint: - # Special case for Py2 short or long int - return isinstance(instance, (int, long)) - else: - return issubclass(instance.__class__, cls) - - -class newint(with_metaclass(BaseNewInt, long)): - """ - A backport of the Python 3 int object to Py2 - """ - def __new__(cls, x=0, base=10): - """ - From the Py3 int docstring: - - | int(x=0) -> integer - | int(x, base=10) -> integer - | - | Convert a number or string to an integer, or return 0 if no - | arguments are given. If x is a number, return x.__int__(). For - | floating point numbers, this truncates towards zero. - | - | If x is not a number or if base is given, then x must be a string, - | bytes, or bytearray instance representing an integer literal in the - | given base. The literal can be preceded by '+' or '-' and be - | surrounded by whitespace. The base defaults to 10. Valid bases are - | 0 and 2-36. Base 0 means to interpret the base from the string as an - | integer literal. - | >>> int('0b100', base=0) - | 4 - - """ - try: - val = x.__int__() - except AttributeError: - val = x - else: - if not isint(val): - raise TypeError('__int__ returned non-int ({0})'.format( - type(val))) - - if base != 10: - # Explicit base - if not (istext(val) or isbytes(val) or isinstance(val, bytearray)): - raise TypeError( - "int() can't convert non-string with explicit base") - try: - return super(newint, cls).__new__(cls, val, base) - except TypeError: - return super(newint, cls).__new__(cls, newbytes(val), base) - # After here, base is 10 - try: - return super(newint, cls).__new__(cls, val) - except TypeError: - # Py2 long doesn't handle bytearray input with an explicit base, so - # handle this here. - # Py3: int(bytearray(b'10'), 2) == 2 - # Py2: int(bytearray(b'10'), 2) == 2 raises TypeError - # Py2: long(bytearray(b'10'), 2) == 2 raises TypeError - try: - return super(newint, cls).__new__(cls, newbytes(val)) - except: - raise TypeError("newint argument must be a string or a number," - "not '{0}'".format(type(val))) - - def __repr__(self): - """ - Without the L suffix - """ - value = super(newint, self).__repr__() - assert value[-1] == 'L' - return value[:-1] - - def __add__(self, other): - value = super(newint, self).__add__(other) - if value is NotImplemented: - return long(self) + other - return newint(value) - - def __radd__(self, other): - value = super(newint, self).__radd__(other) - if value is NotImplemented: - return other + long(self) - return newint(value) - - def __sub__(self, other): - value = super(newint, self).__sub__(other) - if value is NotImplemented: - return long(self) - other - return newint(value) - - def __rsub__(self, other): - value = super(newint, self).__rsub__(other) - if value is NotImplemented: - return other - long(self) - return newint(value) - - def __mul__(self, other): - value = super(newint, self).__mul__(other) - if isint(value): - return newint(value) - elif value is NotImplemented: - return long(self) * other - return value - - def __rmul__(self, other): - value = super(newint, self).__rmul__(other) - if isint(value): - return newint(value) - elif value is NotImplemented: - return other * long(self) - return value - - def __div__(self, other): - # We override this rather than e.g. relying on object.__div__ or - # long.__div__ because we want to wrap the value in a newint() - # call if other is another int - value = long(self) / other - if isinstance(other, (int, long)): - return newint(value) - else: - return value - - def __rdiv__(self, other): - value = other / long(self) - if isinstance(other, (int, long)): - return newint(value) - else: - return value - - def __idiv__(self, other): - # long has no __idiv__ method. Use __itruediv__ and cast back to - # newint: - value = self.__itruediv__(other) - if isinstance(other, (int, long)): - return newint(value) - else: - return value - - def __truediv__(self, other): - value = super(newint, self).__truediv__(other) - if value is NotImplemented: - value = long(self) / other - return value - - def __rtruediv__(self, other): - return super(newint, self).__rtruediv__(other) - - def __itruediv__(self, other): - # long has no __itruediv__ method - mylong = long(self) - mylong /= other - return mylong - - def __floordiv__(self, other): - return newint(super(newint, self).__floordiv__(other)) - - def __rfloordiv__(self, other): - return newint(super(newint, self).__rfloordiv__(other)) - - def __ifloordiv__(self, other): - # long has no __ifloordiv__ method - mylong = long(self) - mylong //= other - return newint(mylong) - - def __mod__(self, other): - value = super(newint, self).__mod__(other) - if value is NotImplemented: - return long(self) % other - return newint(value) - - def __rmod__(self, other): - value = super(newint, self).__rmod__(other) - if value is NotImplemented: - return other % long(self) - return newint(value) - - def __divmod__(self, other): - value = super(newint, self).__divmod__(other) - if value is NotImplemented: - mylong = long(self) - return (mylong // other, mylong % other) - return (newint(value[0]), newint(value[1])) - - def __rdivmod__(self, other): - value = super(newint, self).__rdivmod__(other) - if value is NotImplemented: - mylong = long(self) - return (other // mylong, other % mylong) - return (newint(value[0]), newint(value[1])) - - def __pow__(self, other): - value = super(newint, self).__pow__(other) - if value is NotImplemented: - return long(self) ** other - return newint(value) - - def __rpow__(self, other): - value = super(newint, self).__rpow__(other) - if value is NotImplemented: - return other ** long(self) - return newint(value) - - def __lshift__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for <<: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__lshift__(other)) - - def __rshift__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for >>: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__rshift__(other)) - - def __and__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for &: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__and__(other)) - - def __or__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for |: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__or__(other)) - - def __xor__(self, other): - if not isint(other): - raise TypeError( - "unsupported operand type(s) for ^: '%s' and '%s'" % - (type(self).__name__, type(other).__name__)) - return newint(super(newint, self).__xor__(other)) - - def __neg__(self): - return newint(super(newint, self).__neg__()) - - def __pos__(self): - return newint(super(newint, self).__pos__()) - - def __abs__(self): - return newint(super(newint, self).__abs__()) - - def __invert__(self): - return newint(super(newint, self).__invert__()) - - def __int__(self): - return self - - def __nonzero__(self): - return self.__bool__() - - def __bool__(self): - """ - So subclasses can override this, Py3-style - """ - return super(newint, self).__nonzero__() - - def __native__(self): - return long(self) - - def to_bytes(self, length, byteorder='big', signed=False): - """ - Return an array of bytes representing an integer. - - The integer is represented using length bytes. An OverflowError is - raised if the integer is not representable with the given number of - bytes. - - The byteorder argument determines the byte order used to represent the - integer. If byteorder is 'big', the most significant byte is at the - beginning of the byte array. If byteorder is 'little', the most - significant byte is at the end of the byte array. To request the native - byte order of the host system, use `sys.byteorder' as the byte order value. - - The signed keyword-only argument determines whether two's complement is - used to represent the integer. If signed is False and a negative integer - is given, an OverflowError is raised. - """ - if length < 0: - raise ValueError("length argument must be non-negative") - if length == 0 and self == 0: - return newbytes() - if signed and self < 0: - bits = length * 8 - num = (2**bits) + self - if num <= 0: - raise OverflowError("int too smal to convert") - else: - if self < 0: - raise OverflowError("can't convert negative int to unsigned") - num = self - if byteorder not in ('little', 'big'): - raise ValueError("byteorder must be either 'little' or 'big'") - h = b'%x' % num - s = newbytes((b'0'*(len(h) % 2) + h).zfill(length*2).decode('hex')) - if signed: - high_set = s[0] & 0x80 - if self > 0 and high_set: - raise OverflowError("int too big to convert") - if self < 0 and not high_set: - raise OverflowError("int too small to convert") - if len(s) > length: - raise OverflowError("int too big to convert") - return s if byteorder == 'big' else s[::-1] - - @classmethod - def from_bytes(cls, mybytes, byteorder='big', signed=False): - """ - Return the integer represented by the given array of bytes. - - The mybytes argument must either support the buffer protocol or be an - iterable object producing bytes. Bytes and bytearray are examples of - built-in objects that support the buffer protocol. - - The byteorder argument determines the byte order used to represent the - integer. If byteorder is 'big', the most significant byte is at the - beginning of the byte array. If byteorder is 'little', the most - significant byte is at the end of the byte array. To request the native - byte order of the host system, use `sys.byteorder' as the byte order value. - - The signed keyword-only argument indicates whether two's complement is - used to represent the integer. - """ - if byteorder not in ('little', 'big'): - raise ValueError("byteorder must be either 'little' or 'big'") - if isinstance(mybytes, unicode): - raise TypeError("cannot convert unicode objects to bytes") - # mybytes can also be passed as a sequence of integers on Py3. - # Test for this: - elif isinstance(mybytes, collections.Iterable): - mybytes = newbytes(mybytes) - b = mybytes if byteorder == 'big' else mybytes[::-1] - if len(b) == 0: - b = b'\x00' - # The encode() method has been disabled by newbytes, but Py2's - # str has it: - num = int(native(b).encode('hex'), 16) - if signed and (b[0] & 0x80): - num = num - (2 ** (len(b)*8)) - return cls(num) - - -# def _twos_comp(val, bits): -# """compute the 2's compliment of int value val""" -# if( (val&(1<<(bits-1))) != 0 ): -# val = val - (1<>> from builtins import list ->>> l1 = list() # instead of {} for an empty list ->>> l1.append('hello') ->>> l2 = l1.copy() - -""" - -import sys -import copy - -from future.utils import with_metaclass -from future.types.newobject import newobject - - -_builtin_list = list -ver = sys.version_info[:2] - - -class BaseNewList(type): - def __instancecheck__(cls, instance): - if cls == newlist: - return isinstance(instance, _builtin_list) - else: - return issubclass(instance.__class__, cls) - - -class newlist(with_metaclass(BaseNewList, _builtin_list)): - """ - A backport of the Python 3 list object to Py2 - """ - def copy(self): - """ - L.copy() -> list -- a shallow copy of L - """ - return copy.copy(self) - - def clear(self): - """L.clear() -> None -- remove all items from L""" - for i in range(len(self)): - self.pop() - - def __new__(cls, *args, **kwargs): - """ - list() -> new empty list - list(iterable) -> new list initialized from iterable's items - """ - - if len(args) == 0: - return super(newlist, cls).__new__(cls) - elif type(args[0]) == newlist: - value = args[0] - else: - value = args[0] - return super(newlist, cls).__new__(cls, value) - - def __add__(self, value): - return newlist(super(newlist, self).__add__(value)) - - def __radd__(self, left): - " left + self " - try: - return newlist(left) + self - except: - return NotImplemented - - def __getitem__(self, y): - """ - x.__getitem__(y) <==> x[y] - - Warning: a bug in Python 2.x prevents indexing via a slice from - returning a newlist object. - """ - if isinstance(y, slice): - return newlist(super(newlist, self).__getitem__(y)) - else: - return super(newlist, self).__getitem__(y) - - def __native__(self): - """ - Hook for the future.utils.native() function - """ - return list(self) - - def __nonzero__(self): - return len(self) > 0 - - -__all__ = ['newlist'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newmemoryview.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newmemoryview.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newmemoryview.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newmemoryview.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -""" -A pretty lame implementation of a memoryview object for Python 2.6. -""" - -from collections import Iterable -from numbers import Integral -import string - -from future.utils import istext, isbytes, PY3, with_metaclass -from future.types import no, issubset - - -# class BaseNewBytes(type): -# def __instancecheck__(cls, instance): -# return isinstance(instance, _builtin_bytes) - - -class newmemoryview(object): # with_metaclass(BaseNewBytes, _builtin_bytes)): - """ - A pretty lame backport of the Python 2.7 and Python 3.x - memoryviewview object to Py2.6. - """ - def __init__(self, obj): - return obj - - -__all__ = ['newmemoryview'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newobject.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newobject.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newobject.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newobject.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -""" -An object subclass for Python 2 that gives new-style classes written in the -style of Python 3 (with ``__next__`` and unicode-returning ``__str__`` methods) -the appropriate Python 2-style ``next`` and ``__unicode__`` methods for compatible. - -Example use:: - - from builtins import object - - my_unicode_str = u'Unicode string: \u5b54\u5b50' - - class A(object): - def __str__(self): - return my_unicode_str - - a = A() - print(str(a)) - - # On Python 2, these relations hold: - assert unicode(a) == my_unicode_string - assert str(a) == my_unicode_string.encode('utf-8') - - -Another example:: - - from builtins import object - - class Upper(object): - def __init__(self, iterable): - self._iter = iter(iterable) - def __next__(self): # note the Py3 interface - return next(self._iter).upper() - def __iter__(self): - return self - - assert list(Upper('hello')) == list('HELLO') - -""" - -import sys - -from future.utils import with_metaclass - - -_builtin_object = object -ver = sys.version_info[:2] - - -# We no longer define a metaclass for newobject because this breaks multiple -# inheritance and custom metaclass use with this exception: - -# TypeError: Error when calling the metaclass bases -# metaclass conflict: the metaclass of a derived class must be a -# (non-strict) subclass of the metaclasses of all its bases - -# See issues #91 and #96. - - -class newobject(object): - """ - A magical object class that provides Python 2 compatibility methods:: - next - __unicode__ - __nonzero__ - - Subclasses of this class can merely define the Python 3 methods (__next__, - __str__, and __bool__). - """ - def next(self): - if hasattr(self, '__next__'): - return type(self).__next__(self) - raise TypeError('newobject is not an iterator') - - def __unicode__(self): - # All subclasses of the builtin object should have __str__ defined. - # Note that old-style classes do not have __str__ defined. - if hasattr(self, '__str__'): - s = type(self).__str__(self) - else: - s = str(self) - if isinstance(s, unicode): - return s - else: - return s.decode('utf-8') - - def __nonzero__(self): - if hasattr(self, '__bool__'): - return type(self).__bool__(self) - if hasattr(self, '__len__'): - return type(self).__len__(self) - # object has no __nonzero__ method - return True - - # Are these ever needed? - # def __div__(self): - # return self.__truediv__() - - # def __idiv__(self, other): - # return self.__itruediv__(other) - - def __long__(self): - if not hasattr(self, '__int__'): - return NotImplemented - return self.__int__() # not type(self).__int__(self) - - # def __new__(cls, *args, **kwargs): - # """ - # dict() -> new empty dictionary - # dict(mapping) -> new dictionary initialized from a mapping object's - # (key, value) pairs - # dict(iterable) -> new dictionary initialized as if via: - # d = {} - # for k, v in iterable: - # d[k] = v - # dict(**kwargs) -> new dictionary initialized with the name=value pairs - # in the keyword argument list. For example: dict(one=1, two=2) - # """ - - # if len(args) == 0: - # return super(newdict, cls).__new__(cls) - # elif type(args[0]) == newdict: - # return args[0] - # else: - # value = args[0] - # return super(newdict, cls).__new__(cls, value) - - def __native__(self): - """ - Hook for the future.utils.native() function - """ - return object(self) - - -__all__ = ['newobject'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newopen.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newopen.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newopen.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newopen.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -""" -A substitute for the Python 3 open() function. - -Note that io.open() is more complete but maybe slower. Even so, the -completeness may be a better default. TODO: compare these -""" - -_builtin_open = open - -class newopen(object): - """Wrapper providing key part of Python 3 open() interface. - - From IPython's py3compat.py module. License: BSD. - """ - def __init__(self, fname, mode="r", encoding="utf-8"): - self.f = _builtin_open(fname, mode) - self.enc = encoding - - def write(self, s): - return self.f.write(s.encode(self.enc)) - - def read(self, size=-1): - return self.f.read(size).decode(self.enc) - - def close(self): - return self.f.close() - - def __enter__(self): - return self - - def __exit__(self, etype, value, traceback): - self.f.close() - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newrange.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newrange.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newrange.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newrange.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,159 +0,0 @@ -""" -Nearly identical to xrange.py, by Dan Crosta, from - - https://github.com/dcrosta/xrange.git - -This is included here in the ``future`` package rather than pointed to as -a dependency because there is no package for ``xrange`` on PyPI. It is -also tweaked to appear like a regular Python 3 ``range`` object rather -than a Python 2 xrange. - -From Dan Crosta's README: - - "A pure-Python implementation of Python 2.7's xrange built-in, with - some features backported from the Python 3.x range built-in (which - replaced xrange) in that version." - - Read more at - https://late.am/post/2012/06/18/what-the-heck-is-an-xrange -""" -from __future__ import absolute_import - -from collections import Sequence, Iterator -from itertools import islice - -from future.backports.misc import count # with step parameter on Py2.6 -# For backward compatibility with python-future versions < 0.14.4: -_count = count - - -class newrange(Sequence): - """ - Pure-Python backport of Python 3's range object. See `the CPython - documentation for details: - `_ - """ - - def __init__(self, *args): - if len(args) == 1: - start, stop, step = 0, args[0], 1 - elif len(args) == 2: - start, stop, step = args[0], args[1], 1 - elif len(args) == 3: - start, stop, step = args - else: - raise TypeError('range() requires 1-3 int arguments') - - try: - start, stop, step = int(start), int(stop), int(step) - except ValueError: - raise TypeError('an integer is required') - - if step == 0: - raise ValueError('range() arg 3 must not be zero') - elif step < 0: - stop = min(stop, start) - else: - stop = max(stop, start) - - self._start = start - self._stop = stop - self._step = step - self._len = (stop - start) // step + bool((stop - start) % step) - - @property - def start(self): - return self._start - - @property - def stop(self): - return self._stop - - @property - def step(self): - return self._step - - def __repr__(self): - if self._step == 1: - return 'range(%d, %d)' % (self._start, self._stop) - return 'range(%d, %d, %d)' % (self._start, self._stop, self._step) - - def __eq__(self, other): - return (isinstance(other, newrange) and - (self._len == 0 == other._len or - (self._start, self._step, self._len) == - (other._start, other._step, self._len))) - - def __len__(self): - return self._len - - def index(self, value): - """Return the 0-based position of integer `value` in - the sequence this range represents.""" - diff = value - self._start - quotient, remainder = divmod(diff, self._step) - if remainder == 0 and 0 <= quotient < self._len: - return abs(quotient) - raise ValueError('%r is not in range' % value) - - def count(self, value): - """Return the number of ocurrences of integer `value` - in the sequence this range represents.""" - # a value can occur exactly zero or one times - return int(value in self) - - def __contains__(self, value): - """Return ``True`` if the integer `value` occurs in - the sequence this range represents.""" - try: - self.index(value) - return True - except ValueError: - return False - - def __reversed__(self): - return iter(self[::-1]) - - def __getitem__(self, index): - """Return the element at position ``index`` in the sequence - this range represents, or raise :class:`IndexError` if the - position is out of range.""" - if isinstance(index, slice): - return self.__getitem_slice(index) - if index < 0: - # negative indexes access from the end - index = self._len + index - if index < 0 or index >= self._len: - raise IndexError('range object index out of range') - return self._start + index * self._step - - def __getitem_slice(self, slce): - """Return a range which represents the requested slce - of the sequence represented by this range. - """ - scaled_indices = (self._step * n for n in slce.indices(self._len)) - start_offset, stop_offset, new_step = scaled_indices - return newrange(self._start + start_offset, - self._start + stop_offset, - new_step) - - def __iter__(self): - """Return an iterator which enumerates the elements of the - sequence this range represents.""" - return range_iterator(self) - - -class range_iterator(Iterator): - """An iterator for a :class:`range`. - """ - def __init__(self, range_): - self._stepper = islice(count(range_.start, range_.step), len(range_)) - - def __iter__(self): - return self - - def next(self): - return next(self._stepper) - - -__all__ = ['newrange'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newstr.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newstr.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/types/newstr.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/types/newstr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,412 +0,0 @@ -""" -This module redefines ``str`` on Python 2.x to be a subclass of the Py2 -``unicode`` type that behaves like the Python 3.x ``str``. - -The main differences between ``newstr`` and Python 2.x's ``unicode`` type are -the stricter type-checking and absence of a `u''` prefix in the representation. - -It is designed to be used together with the ``unicode_literals`` import -as follows: - - >>> from __future__ import unicode_literals - >>> from builtins import str, isinstance - -On Python 3.x and normally on Python 2.x, these expressions hold - - >>> str('blah') is 'blah' - True - >>> isinstance('blah', str) - True - -However, on Python 2.x, with this import: - - >>> from __future__ import unicode_literals - -the same expressions are False: - - >>> str('blah') is 'blah' - False - >>> isinstance('blah', str) - False - -This module is designed to be imported together with ``unicode_literals`` on -Python 2 to bring the meaning of ``str`` back into alignment with unprefixed -string literals (i.e. ``unicode`` subclasses). - -Note that ``str()`` (and ``print()``) would then normally call the -``__unicode__`` method on objects in Python 2. To define string -representations of your objects portably across Py3 and Py2, use the -:func:`python_2_unicode_compatible` decorator in :mod:`future.utils`. - -""" - -from collections import Iterable -from numbers import Number - -from future.utils import PY3, istext, with_metaclass, isnewbytes -from future.types import no, issubset -from future.types.newobject import newobject - - -if PY3: - # We'll probably never use newstr on Py3 anyway... - unicode = str - - -class BaseNewStr(type): - def __instancecheck__(cls, instance): - if cls == newstr: - return isinstance(instance, unicode) - else: - return issubclass(instance.__class__, cls) - - -class newstr(with_metaclass(BaseNewStr, unicode)): - """ - A backport of the Python 3 str object to Py2 - """ - no_convert_msg = "Can't convert '{0}' object to str implicitly" - - def __new__(cls, *args, **kwargs): - """ - From the Py3 str docstring: - - str(object='') -> str - str(bytes_or_buffer[, encoding[, errors]]) -> str - - Create a new string object from the given object. If encoding or - errors is specified, then the object must expose a data buffer - that will be decoded using the given encoding and error handler. - Otherwise, returns the result of object.__str__() (if defined) - or repr(object). - encoding defaults to sys.getdefaultencoding(). - errors defaults to 'strict'. - - """ - if len(args) == 0: - return super(newstr, cls).__new__(cls) - # Special case: If someone requests str(str(u'abc')), return the same - # object (same id) for consistency with Py3.3. This is not true for - # other objects like list or dict. - elif type(args[0]) == newstr and cls == newstr: - return args[0] - elif isinstance(args[0], unicode): - value = args[0] - elif isinstance(args[0], bytes): # i.e. Py2 bytes or newbytes - if 'encoding' in kwargs or len(args) > 1: - value = args[0].decode(*args[1:], **kwargs) - else: - value = args[0].__str__() - else: - value = args[0] - return super(newstr, cls).__new__(cls, value) - - def __repr__(self): - """ - Without the u prefix - """ - value = super(newstr, self).__repr__() - # assert value[0] == u'u' - return value[1:] - - def __getitem__(self, y): - """ - Warning: Python <= 2.7.6 has a bug that causes this method never to be called - when y is a slice object. Therefore the type of newstr()[:2] is wrong - (unicode instead of newstr). - """ - return newstr(super(newstr, self).__getitem__(y)) - - def __contains__(self, key): - errmsg = "'in ' requires string as left operand, not {0}" - # Don't use isinstance() here because we only want to catch - # newstr, not Python 2 unicode: - if type(key) == newstr: - newkey = key - elif isinstance(key, unicode) or isinstance(key, bytes) and not isnewbytes(key): - newkey = newstr(key) - else: - raise TypeError(errmsg.format(type(key))) - return issubset(list(newkey), list(self)) - - @no('newbytes') - def __add__(self, other): - return newstr(super(newstr, self).__add__(other)) - - @no('newbytes') - def __radd__(self, left): - " left + self " - try: - return newstr(left) + self - except: - return NotImplemented - - def __mul__(self, other): - return newstr(super(newstr, self).__mul__(other)) - - def __rmul__(self, other): - return newstr(super(newstr, self).__rmul__(other)) - - def join(self, iterable): - errmsg = 'sequence item {0}: expected unicode string, found bytes' - for i, item in enumerate(iterable): - # Here we use type() rather than isinstance() because - # __instancecheck__ is being overridden. E.g. - # isinstance(b'abc', newbytes) is True on Py2. - if isnewbytes(item): - raise TypeError(errmsg.format(i)) - # Support use as a staticmethod: str.join('-', ['a', 'b']) - if type(self) == newstr: - return newstr(super(newstr, self).join(iterable)) - else: - return newstr(super(newstr, newstr(self)).join(iterable)) - - @no('newbytes') - def find(self, sub, *args): - return super(newstr, self).find(sub, *args) - - @no('newbytes') - def rfind(self, sub, *args): - return super(newstr, self).rfind(sub, *args) - - @no('newbytes', (1, 2)) - def replace(self, old, new, *args): - return newstr(super(newstr, self).replace(old, new, *args)) - - def decode(self, *args): - raise AttributeError("decode method has been disabled in newstr") - - def encode(self, encoding='utf-8', errors='strict'): - """ - Returns bytes - - Encode S using the codec registered for encoding. Default encoding - is 'utf-8'. errors may be given to set a different error - handling scheme. Default is 'strict' meaning that encoding errors raise - a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and - 'xmlcharrefreplace' as well as any other name registered with - codecs.register_error that can handle UnicodeEncodeErrors. - """ - from future.types.newbytes import newbytes - # Py2 unicode.encode() takes encoding and errors as optional parameter, - # not keyword arguments as in Python 3 str. - - # For the surrogateescape error handling mechanism, the - # codecs.register_error() function seems to be inadequate for an - # implementation of it when encoding. (Decoding seems fine, however.) - # For example, in the case of - # u'\udcc3'.encode('ascii', 'surrogateescape_handler') - # after registering the ``surrogateescape_handler`` function in - # future.utils.surrogateescape, both Python 2.x and 3.x raise an - # exception anyway after the function is called because the unicode - # string it has to return isn't encodable strictly as ASCII. - - if errors == 'surrogateescape': - if encoding == 'utf-16': - # Known to fail here. See test_encoding_works_normally() - raise NotImplementedError('FIXME: surrogateescape handling is ' - 'not yet implemented properly') - # Encode char by char, building up list of byte-strings - mybytes = [] - for c in self: - code = ord(c) - if 0xD800 <= code <= 0xDCFF: - mybytes.append(newbytes([code - 0xDC00])) - else: - mybytes.append(c.encode(encoding=encoding)) - return newbytes(b'').join(mybytes) - return newbytes(super(newstr, self).encode(encoding, errors)) - - @no('newbytes', 1) - def startswith(self, prefix, *args): - if isinstance(prefix, Iterable): - for thing in prefix: - if isnewbytes(thing): - raise TypeError(self.no_convert_msg.format(type(thing))) - return super(newstr, self).startswith(prefix, *args) - - @no('newbytes', 1) - def endswith(self, prefix, *args): - # Note we need the decorator above as well as the isnewbytes() - # check because prefix can be either a bytes object or e.g. a - # tuple of possible prefixes. (If it's a bytes object, each item - # in it is an int.) - if isinstance(prefix, Iterable): - for thing in prefix: - if isnewbytes(thing): - raise TypeError(self.no_convert_msg.format(type(thing))) - return super(newstr, self).endswith(prefix, *args) - - @no('newbytes', 1) - def split(self, sep=None, maxsplit=-1): - # Py2 unicode.split() takes maxsplit as an optional parameter, - # not as a keyword argument as in Python 3 str. - parts = super(newstr, self).split(sep, maxsplit) - return [newstr(part) for part in parts] - - @no('newbytes', 1) - def rsplit(self, sep=None, maxsplit=-1): - # Py2 unicode.rsplit() takes maxsplit as an optional parameter, - # not as a keyword argument as in Python 3 str. - parts = super(newstr, self).rsplit(sep, maxsplit) - return [newstr(part) for part in parts] - - @no('newbytes', 1) - def partition(self, sep): - parts = super(newstr, self).partition(sep) - return tuple(newstr(part) for part in parts) - - @no('newbytes', 1) - def rpartition(self, sep): - parts = super(newstr, self).rpartition(sep) - return tuple(newstr(part) for part in parts) - - @no('newbytes', 1) - def index(self, sub, *args): - """ - Like newstr.find() but raise ValueError when the substring is not - found. - """ - pos = self.find(sub, *args) - if pos == -1: - raise ValueError('substring not found') - return pos - - def splitlines(self, keepends=False): - """ - S.splitlines(keepends=False) -> list of strings - - Return a list of the lines in S, breaking at line boundaries. - Line breaks are not included in the resulting list unless keepends - is given and true. - """ - # Py2 unicode.splitlines() takes keepends as an optional parameter, - # not as a keyword argument as in Python 3 str. - parts = super(newstr, self).splitlines(keepends) - return [newstr(part) for part in parts] - - def __eq__(self, other): - if (isinstance(other, unicode) or - isinstance(other, bytes) and not isnewbytes(other)): - return super(newstr, self).__eq__(other) - else: - return False - - def __ne__(self, other): - if (isinstance(other, unicode) or - isinstance(other, bytes) and not isnewbytes(other)): - return super(newstr, self).__ne__(other) - else: - return True - - unorderable_err = 'unorderable types: str() and {0}' - - def __lt__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__lt__(other) - - def __le__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__le__(other) - - def __gt__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__gt__(other) - - def __ge__(self, other): - if not istext(other): - raise TypeError(self.unorderable_err.format(type(other))) - return super(newstr, self).__ge__(other) - - def __getattribute__(self, name): - """ - A trick to cause the ``hasattr`` builtin-fn to return False for - the 'decode' method on Py2. - """ - if name in ['decode', u'decode']: - raise AttributeError("decode method has been disabled in newstr") - return super(newstr, self).__getattribute__(name) - - def __native__(self): - """ - A hook for the future.utils.native() function. - """ - return unicode(self) - - @staticmethod - def maketrans(x, y=None, z=None): - """ - Return a translation table usable for str.translate(). - - If there is only one argument, it must be a dictionary mapping Unicode - ordinals (integers) or characters to Unicode ordinals, strings or None. - Character keys will be then converted to ordinals. - If there are two arguments, they must be strings of equal length, and - in the resulting dictionary, each character in x will be mapped to the - character at the same position in y. If there is a third argument, it - must be a string, whose characters will be mapped to None in the result. - """ - - if y is None: - assert z is None - if not isinstance(x, dict): - raise TypeError('if you give only one argument to maketrans it must be a dict') - result = {} - for (key, value) in x.items(): - if len(key) > 1: - raise ValueError('keys in translate table must be strings or integers') - result[ord(key)] = value - else: - if not isinstance(x, unicode) and isinstance(y, unicode): - raise TypeError('x and y must be unicode strings') - if not len(x) == len(y): - raise ValueError('the first two maketrans arguments must have equal length') - result = {} - for (xi, yi) in zip(x, y): - if len(xi) > 1: - raise ValueError('keys in translate table must be strings or integers') - result[ord(xi)] = ord(yi) - - if z is not None: - for char in z: - result[ord(char)] = None - return result - - def translate(self, table): - """ - S.translate(table) -> str - - Return a copy of the string S, where all characters have been mapped - through the given translation table, which must be a mapping of - Unicode ordinals to Unicode ordinals, strings, or None. - Unmapped characters are left untouched. Characters mapped to None - are deleted. - """ - l = [] - for c in self: - if ord(c) in table: - val = table[ord(c)] - if val is None: - continue - elif isinstance(val, unicode): - l.append(val) - else: - l.append(chr(val)) - else: - l.append(c) - return ''.join(l) - - def isprintable(self): - raise NotImplementedError('fixme') - - def isidentifier(self): - raise NotImplementedError('fixme') - - def format_map(self): - raise NotImplementedError('fixme') - - -__all__ = ['newstr'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/utils/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/utils/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/utils/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/utils/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,739 +0,0 @@ -""" -A selection of cross-compatible functions for Python 2 and 3. - -This module exports useful functions for 2/3 compatible code: - - * bind_method: binds functions to classes - * ``native_str_to_bytes`` and ``bytes_to_native_str`` - * ``native_str``: always equal to the native platform string object (because - this may be shadowed by imports from future.builtins) - * lists: lrange(), lmap(), lzip(), lfilter() - * iterable method compatibility: - - iteritems, iterkeys, itervalues - - viewitems, viewkeys, viewvalues - - These use the original method if available, otherwise they use items, - keys, values. - - * types: - - * text_type: unicode in Python 2, str in Python 3 - * binary_type: str in Python 2, bythes in Python 3 - * string_types: basestring in Python 2, str in Python 3 - - * bchr(c): - Take an integer and make a 1-character byte string - * bord(c) - Take the result of indexing on a byte string and make an integer - * tobytes(s) - Take a text string, a byte string, or a sequence of characters taken - from a byte string, and make a byte string. - - * raise_from() - * raise_with_traceback() - -This module also defines these decorators: - - * ``python_2_unicode_compatible`` - * ``with_metaclass`` - * ``implements_iterator`` - -Some of the functions in this module come from the following sources: - - * Jinja2 (BSD licensed: see - https://github.com/mitsuhiko/jinja2/blob/master/LICENSE) - * Pandas compatibility module pandas.compat - * six.py by Benjamin Peterson - * Django -""" - -import types -import sys -import numbers -import functools -import copy -import inspect - - -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 -PY26 = sys.version_info[0:2] == (2, 6) -PY27 = sys.version_info[0:2] == (2, 7) -PYPY = hasattr(sys, 'pypy_translation_info') - - -def python_2_unicode_compatible(cls): - """ - A decorator that defines __unicode__ and __str__ methods under Python - 2. Under Python 3, this decorator is a no-op. - - To support Python 2 and 3 with a single code base, define a __str__ - method returning unicode text and apply this decorator to the class, like - this:: - - >>> from future.utils import python_2_unicode_compatible - - >>> @python_2_unicode_compatible - ... class MyClass(object): - ... def __str__(self): - ... return u'Unicode string: \u5b54\u5b50' - - >>> a = MyClass() - - Then, after this import: - - >>> from future.builtins import str - - the following is ``True`` on both Python 3 and 2:: - - >>> str(a) == a.encode('utf-8').decode('utf-8') - True - - and, on a Unicode-enabled terminal with the right fonts, these both print the - Chinese characters for Confucius:: - - >>> print(a) - >>> print(str(a)) - - The implementation comes from django.utils.encoding. - """ - if not PY3: - cls.__unicode__ = cls.__str__ - cls.__str__ = lambda self: self.__unicode__().encode('utf-8') - return cls - - -def with_metaclass(meta, *bases): - """ - Function from jinja2/_compat.py. License: BSD. - - Use it like this:: - - class BaseForm(object): - pass - - class FormType(type): - pass - - class Form(with_metaclass(FormType, BaseForm)): - pass - - 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. Because of internal type checks - we also need to make sure that we downgrade the custom metaclass - for one level to something closer to type (that's why __call__ and - __init__ comes back from type etc.). - - This has the advantage over six.with_metaclass of not introducing - dummy classes into the final MRO. - """ - class metaclass(meta): - __call__ = type.__call__ - __init__ = type.__init__ - def __new__(cls, name, this_bases, d): - if this_bases is None: - return type.__new__(cls, name, (), d) - return meta(name, bases, d) - return metaclass('temporary_class', None, {}) - - -# Definitions from pandas.compat and six.py follow: -if PY3: - def bchr(s): - return bytes([s]) - def bstr(s): - if isinstance(s, str): - return bytes(s, 'latin-1') - else: - return bytes(s) - def bord(s): - return s - - string_types = str, - integer_types = int, - class_types = type, - text_type = str - binary_type = bytes - -else: - # Python 2 - def bchr(s): - return chr(s) - def bstr(s): - return str(s) - def bord(s): - return ord(s) - - string_types = basestring, - integer_types = (int, long) - class_types = (type, types.ClassType) - text_type = unicode - binary_type = str - -### - -if PY3: - def tobytes(s): - if isinstance(s, bytes): - return s - else: - if isinstance(s, str): - return s.encode('latin-1') - else: - return bytes(s) -else: - # Python 2 - def tobytes(s): - if isinstance(s, unicode): - return s.encode('latin-1') - else: - return ''.join(s) - -tobytes.__doc__ = """ - Encodes to latin-1 (where the first 256 chars are the same as - ASCII.) - """ - -if PY3: - def native_str_to_bytes(s, encoding='utf-8'): - return s.encode(encoding) - - def bytes_to_native_str(b, encoding='utf-8'): - return b.decode(encoding) - - def text_to_native_str(t, encoding=None): - return t -else: - # Python 2 - def native_str_to_bytes(s, encoding=None): - from future.types import newbytes # to avoid a circular import - return newbytes(s) - - def bytes_to_native_str(b, encoding=None): - return native(b) - - def text_to_native_str(t, encoding='ascii'): - """ - Use this to create a Py2 native string when "from __future__ import - unicode_literals" is in effect. - """ - return unicode(t).encode(encoding) - -native_str_to_bytes.__doc__ = """ - On Py3, returns an encoded string. - On Py2, returns a newbytes type, ignoring the ``encoding`` argument. - """ - -if PY3: - # list-producing versions of the major Python iterating functions - def lrange(*args, **kwargs): - return list(range(*args, **kwargs)) - - def lzip(*args, **kwargs): - return list(zip(*args, **kwargs)) - - def lmap(*args, **kwargs): - return list(map(*args, **kwargs)) - - def lfilter(*args, **kwargs): - return list(filter(*args, **kwargs)) -else: - import __builtin__ - # Python 2-builtin ranges produce lists - lrange = __builtin__.range - lzip = __builtin__.zip - lmap = __builtin__.map - lfilter = __builtin__.filter - - -def isidentifier(s, dotted=False): - ''' - A function equivalent to the str.isidentifier method on Py3 - ''' - if dotted: - return all(isidentifier(a) for a in s.split('.')) - if PY3: - return s.isidentifier() - else: - import re - _name_re = re.compile(r"[a-zA-Z_][a-zA-Z0-9_]*$") - return bool(_name_re.match(s)) - - -def viewitems(obj, **kwargs): - """ - Function for iterating over dictionary items with the same set-like - behaviour on Py2.7 as on Py3. - - Passes kwargs to method.""" - func = getattr(obj, "viewitems", None) - if not func: - func = obj.items - return func(**kwargs) - - -def viewkeys(obj, **kwargs): - """ - Function for iterating over dictionary keys with the same set-like - behaviour on Py2.7 as on Py3. - - Passes kwargs to method.""" - func = getattr(obj, "viewkeys", None) - if not func: - func = obj.keys - return func(**kwargs) - - -def viewvalues(obj, **kwargs): - """ - Function for iterating over dictionary values with the same set-like - behaviour on Py2.7 as on Py3. - - Passes kwargs to method.""" - func = getattr(obj, "viewvalues", None) - if not func: - func = obj.values - return func(**kwargs) - - -def iteritems(obj, **kwargs): - """Use this only if compatibility with Python versions before 2.7 is - required. Otherwise, prefer viewitems(). - """ - func = getattr(obj, "iteritems", None) - if not func: - func = obj.items - return func(**kwargs) - - -def iterkeys(obj, **kwargs): - """Use this only if compatibility with Python versions before 2.7 is - required. Otherwise, prefer viewkeys(). - """ - func = getattr(obj, "iterkeys", None) - if not func: - func = obj.keys - return func(**kwargs) - - -def itervalues(obj, **kwargs): - """Use this only if compatibility with Python versions before 2.7 is - required. Otherwise, prefer viewvalues(). - """ - func = getattr(obj, "itervalues", None) - if not func: - func = obj.values - return func(**kwargs) - - -def bind_method(cls, name, func): - """Bind a method to class, python 2 and python 3 compatible. - - Parameters - ---------- - - cls : type - class to receive bound method - name : basestring - name of method on class instance - func : function - function to be bound as method - - Returns - ------- - None - """ - # only python 2 has an issue with bound/unbound methods - if not PY3: - setattr(cls, name, types.MethodType(func, None, cls)) - else: - setattr(cls, name, func) - - -def getexception(): - return sys.exc_info()[1] - - -def _get_caller_globals_and_locals(): - """ - Returns the globals and locals of the calling frame. - - Is there an alternative to frame hacking here? - """ - caller_frame = inspect.stack()[2] - myglobals = caller_frame[0].f_globals - mylocals = caller_frame[0].f_locals - return myglobals, mylocals - - -def _repr_strip(mystring): - """ - Returns the string without any initial or final quotes. - """ - r = repr(mystring) - if r.startswith("'") and r.endswith("'"): - return r[1:-1] - else: - return r - - -if PY3: - def raise_from(exc, cause): - """ - Equivalent to: - - raise EXCEPTION from CAUSE - - on Python 3. (See PEP 3134). - """ - myglobals, mylocals = _get_caller_globals_and_locals() - - # We pass the exception and cause along with other globals - # when we exec(): - myglobals = myglobals.copy() - myglobals['__python_future_raise_from_exc'] = exc - myglobals['__python_future_raise_from_cause'] = cause - execstr = "raise __python_future_raise_from_exc from __python_future_raise_from_cause" - exec(execstr, myglobals, mylocals) - - def raise_(tp, value=None, tb=None): - """ - A function that matches the Python 2.x ``raise`` statement. This - allows re-raising exceptions with the cls value and traceback on - Python 2 and 3. - """ - if value is not None and isinstance(tp, Exception): - raise TypeError("instance exception may not have a separate value") - if value is not None: - exc = tp(value) - else: - exc = tp - if exc.__traceback__ is not tb: - raise exc.with_traceback(tb) - raise exc - - def raise_with_traceback(exc, traceback=Ellipsis): - if traceback == Ellipsis: - _, _, traceback = sys.exc_info() - raise exc.with_traceback(traceback) - -else: - def raise_from(exc, cause): - """ - Equivalent to: - - raise EXCEPTION from CAUSE - - on Python 3. (See PEP 3134). - """ - # Is either arg an exception class (e.g. IndexError) rather than - # instance (e.g. IndexError('my message here')? If so, pass the - # name of the class undisturbed through to "raise ... from ...". - if isinstance(exc, type) and issubclass(exc, Exception): - e = exc() - # exc = exc.__name__ - # execstr = "e = " + _repr_strip(exc) + "()" - # myglobals, mylocals = _get_caller_globals_and_locals() - # exec(execstr, myglobals, mylocals) - else: - e = exc - e.__suppress_context__ = False - if isinstance(cause, type) and issubclass(cause, Exception): - e.__cause__ = cause() - e.__suppress_context__ = True - elif cause is None: - e.__cause__ = None - e.__suppress_context__ = True - elif isinstance(cause, BaseException): - e.__cause__ = cause - e.__suppress_context__ = True - else: - raise TypeError("exception causes must derive from BaseException") - e.__context__ = sys.exc_info()[1] - raise e - - exec(''' -def raise_(tp, value=None, tb=None): - raise tp, value, tb - -def raise_with_traceback(exc, traceback=Ellipsis): - if traceback == Ellipsis: - _, _, traceback = sys.exc_info() - raise exc, None, traceback -'''.strip()) - - -raise_with_traceback.__doc__ = ( -"""Raise exception with existing traceback. -If traceback is not passed, uses sys.exc_info() to get traceback.""" -) - - -# Deprecated alias for backward compatibility with ``future`` versions < 0.11: -reraise = raise_ - - -def implements_iterator(cls): - ''' - From jinja2/_compat.py. License: BSD. - - Use as a decorator like this:: - - @implements_iterator - class UppercasingIterator(object): - def __init__(self, iterable): - self._iter = iter(iterable) - def __iter__(self): - return self - def __next__(self): - return next(self._iter).upper() - - ''' - if PY3: - return cls - else: - cls.next = cls.__next__ - del cls.__next__ - return cls - -if PY3: - get_next = lambda x: x.next -else: - get_next = lambda x: x.__next__ - - -def encode_filename(filename): - if PY3: - return filename - else: - if isinstance(filename, unicode): - return filename.encode('utf-8') - return filename - - -def is_new_style(cls): - """ - Python 2.7 has both new-style and old-style classes. Old-style classes can - be pesky in some circumstances, such as when using inheritance. Use this - function to test for whether a class is new-style. (Python 3 only has - new-style classes.) - """ - return hasattr(cls, '__class__') and ('__dict__' in dir(cls) - or hasattr(cls, '__slots__')) - -# The native platform string and bytes types. Useful because ``str`` and -# ``bytes`` are redefined on Py2 by ``from future.builtins import *``. -native_str = str -native_bytes = bytes - - -def istext(obj): - """ - Deprecated. Use:: - >>> isinstance(obj, str) - after this import: - >>> from future.builtins import str - """ - return isinstance(obj, type(u'')) - - -def isbytes(obj): - """ - Deprecated. Use:: - >>> isinstance(obj, bytes) - after this import: - >>> from future.builtins import bytes - """ - return isinstance(obj, type(b'')) - - -def isnewbytes(obj): - """ - Equivalent to the result of ``isinstance(obj, newbytes)`` were - ``__instancecheck__`` not overridden on the newbytes subclass. In - other words, it is REALLY a newbytes instance, not a Py2 native str - object? - """ - # TODO: generalize this so that it works with subclasses of newbytes - # Import is here to avoid circular imports: - from future.types.newbytes import newbytes - return type(obj) == newbytes - - -def isint(obj): - """ - Deprecated. Tests whether an object is a Py3 ``int`` or either a Py2 ``int`` or - ``long``. - - Instead of using this function, you can use: - - >>> from future.builtins import int - >>> isinstance(obj, int) - - The following idiom is equivalent: - - >>> from numbers import Integral - >>> isinstance(obj, Integral) - """ - - return isinstance(obj, numbers.Integral) - - -def native(obj): - """ - On Py3, this is a no-op: native(obj) -> obj - - On Py2, returns the corresponding native Py2 types that are - superclasses for backported objects from Py3: - - >>> from builtins import str, bytes, int - - >>> native(str(u'ABC')) - u'ABC' - >>> type(native(str(u'ABC'))) - unicode - - >>> native(bytes(b'ABC')) - b'ABC' - >>> type(native(bytes(b'ABC'))) - bytes - - >>> native(int(10**20)) - 100000000000000000000L - >>> type(native(int(10**20))) - long - - Existing native types on Py2 will be returned unchanged: - - >>> type(native(u'ABC')) - unicode - """ - if hasattr(obj, '__native__'): - return obj.__native__() - else: - return obj - - -# Implementation of exec_ is from ``six``: -if PY3: - import builtins - exec_ = getattr(builtins, "exec") -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""") - - -# Defined here for backward compatibility: -def old_div(a, b): - """ - DEPRECATED: import ``old_div`` from ``past.utils`` instead. - - Equivalent to ``a / b`` on Python 2 without ``from __future__ import - division``. - - TODO: generalize this to other objects (like arrays etc.) - """ - if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral): - return a // b - else: - return a / b - - -def as_native_str(encoding='utf-8'): - ''' - A decorator to turn a function or method call that returns text, i.e. - unicode, into one that returns a native platform str. - - Use it as a decorator like this:: - - from __future__ import unicode_literals - - class MyClass(object): - @as_native_str(encoding='ascii') - def __repr__(self): - return next(self._iter).upper() - ''' - if PY3: - return lambda f: f - else: - def encoder(f): - @functools.wraps(f) - def wrapper(*args, **kwargs): - return f(*args, **kwargs).encode(encoding=encoding) - return wrapper - return encoder - -# listvalues and listitems definitions from Nick Coghlan's (withdrawn) -# PEP 496: -try: - dict.iteritems -except AttributeError: - # Python 3 - def listvalues(d): - return list(d.values()) - def listitems(d): - return list(d.items()) -else: - # Python 2 - def listvalues(d): - return d.values() - def listitems(d): - return d.items() - -if PY3: - def ensure_new_type(obj): - return obj -else: - def ensure_new_type(obj): - from future.types.newbytes import newbytes - from future.types.newstr import newstr - from future.types.newint import newint - from future.types.newdict import newdict - - native_type = type(native(obj)) - - # Upcast only if the type is already a native (non-future) type - if issubclass(native_type, type(obj)): - # Upcast - if native_type == str: # i.e. Py2 8-bit str - return newbytes(obj) - elif native_type == unicode: - return newstr(obj) - elif native_type == int: - return newint(obj) - elif native_type == long: - return newint(obj) - elif native_type == dict: - return newdict(obj) - else: - return obj - else: - # Already a new type - assert type(obj) in [newbytes, newstr] - return obj - - -__all__ = ['PY2', 'PY26', 'PY3', 'PYPY', - 'as_native_str', 'bind_method', 'bord', 'bstr', - 'bytes_to_native_str', 'encode_filename', 'ensure_new_type', - 'exec_', 'get_next', 'getexception', 'implements_iterator', - 'is_new_style', 'isbytes', 'isidentifier', 'isint', - 'isnewbytes', 'istext', 'iteritems', 'iterkeys', 'itervalues', - 'lfilter', 'listitems', 'listvalues', 'lmap', 'lrange', - 'lzip', 'native', 'native_bytes', 'native_str', - 'native_str_to_bytes', 'old_div', - 'python_2_unicode_compatible', 'raise_', - 'raise_with_traceback', 'reraise', 'text_to_native_str', - 'tobytes', 'viewitems', 'viewkeys', 'viewvalues', - 'with_metaclass' - ] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/future/utils/surrogateescape.py pyglet-1.5.14/tests/extlibs/future/py2_3/future/utils/surrogateescape.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/future/utils/surrogateescape.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/future/utils/surrogateescape.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,200 +0,0 @@ -""" -This is Victor Stinner's pure-Python implementation of PEP 383: the "surrogateescape" error -handler of Python 3. - -Source: misc/python/surrogateescape.py in https://bitbucket.org/haypo/misc -""" - -# This code is released under the Python license and the BSD 2-clause license - -import codecs -import sys - -from future import utils - - -FS_ERRORS = 'surrogateescape' - -# # -- Python 2/3 compatibility ------------------------------------- -# FS_ERRORS = 'my_surrogateescape' - -def u(text): - if utils.PY3: - return text - else: - return text.decode('unicode_escape') - -def b(data): - if utils.PY3: - return data.encode('latin1') - else: - return data - -if utils.PY3: - _unichr = chr - bytes_chr = lambda code: bytes((code,)) -else: - _unichr = unichr - bytes_chr = chr - -def surrogateescape_handler(exc): - """ - Pure Python implementation of the PEP 383: the "surrogateescape" error - handler of Python 3. Undecodable bytes will be replaced by a Unicode - character U+DCxx on decoding, and these are translated into the - original bytes on encoding. - """ - mystring = exc.object[exc.start:exc.end] - - try: - if isinstance(exc, UnicodeDecodeError): - # mystring is a byte-string in this case - decoded = replace_surrogate_decode(mystring) - elif isinstance(exc, UnicodeEncodeError): - # In the case of u'\udcc3'.encode('ascii', - # 'this_surrogateescape_handler'), both Python 2.x and 3.x raise an - # exception anyway after this function is called, even though I think - # it's doing what it should. It seems that the strict encoder is called - # to encode the unicode string that this function returns ... - decoded = replace_surrogate_encode(mystring) - else: - raise exc - except NotASurrogateError: - raise exc - return (decoded, exc.end) - - -class NotASurrogateError(Exception): - pass - - -def replace_surrogate_encode(mystring): - """ - Returns a (unicode) string, not the more logical bytes, because the codecs - register_error functionality expects this. - """ - decoded = [] - for ch in mystring: - # if utils.PY3: - # code = ch - # else: - code = ord(ch) - - # The following magic comes from Py3.3's Python/codecs.c file: - if not 0xD800 <= code <= 0xDCFF: - # Not a surrogate. Fail with the original exception. - raise NotASurrogateError - # mybytes = [0xe0 | (code >> 12), - # 0x80 | ((code >> 6) & 0x3f), - # 0x80 | (code & 0x3f)] - # Is this a good idea? - if 0xDC00 <= code <= 0xDC7F: - decoded.append(_unichr(code - 0xDC00)) - elif code <= 0xDCFF: - decoded.append(_unichr(code - 0xDC00)) - else: - raise NotASurrogateError - return str().join(decoded) - - -def replace_surrogate_decode(mybytes): - """ - Returns a (unicode) string - """ - decoded = [] - for ch in mybytes: - # We may be parsing newbytes (in which case ch is an int) or a native - # str on Py2 - if isinstance(ch, int): - code = ch - else: - code = ord(ch) - if 0x80 <= code <= 0xFF: - decoded.append(_unichr(0xDC00 + code)) - elif code <= 0x7F: - decoded.append(_unichr(code)) - else: - # # It may be a bad byte - # # Try swallowing it. - # continue - # print("RAISE!") - raise NotASurrogateError - return str().join(decoded) - - -def encodefilename(fn): - if FS_ENCODING == 'ascii': - # ASCII encoder of Python 2 expects that the error handler returns a - # Unicode string encodable to ASCII, whereas our surrogateescape error - # handler has to return bytes in 0x80-0xFF range. - encoded = [] - for index, ch in enumerate(fn): - code = ord(ch) - if code < 128: - ch = bytes_chr(code) - elif 0xDC80 <= code <= 0xDCFF: - ch = bytes_chr(code - 0xDC00) - else: - raise UnicodeEncodeError(FS_ENCODING, - fn, index, index+1, - 'ordinal not in range(128)') - encoded.append(ch) - return bytes().join(encoded) - elif FS_ENCODING == 'utf-8': - # UTF-8 encoder of Python 2 encodes surrogates, so U+DC80-U+DCFF - # doesn't go through our error handler - encoded = [] - for index, ch in enumerate(fn): - code = ord(ch) - if 0xD800 <= code <= 0xDFFF: - if 0xDC80 <= code <= 0xDCFF: - ch = bytes_chr(code - 0xDC00) - encoded.append(ch) - else: - raise UnicodeEncodeError( - FS_ENCODING, - fn, index, index+1, 'surrogates not allowed') - else: - ch_utf8 = ch.encode('utf-8') - encoded.append(ch_utf8) - return bytes().join(encoded) - else: - return fn.encode(FS_ENCODING, FS_ERRORS) - -def decodefilename(fn): - return fn.decode(FS_ENCODING, FS_ERRORS) - -FS_ENCODING = 'ascii'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]') -# FS_ENCODING = 'cp932'; fn = b('[abc\x81\x00]'); encoded = u('[abc\udc81\x00]') -# FS_ENCODING = 'UTF-8'; fn = b('[abc\xff]'); encoded = u('[abc\udcff]') - - -# normalize the filesystem encoding name. -# For example, we expect "utf-8", not "UTF8". -FS_ENCODING = codecs.lookup(FS_ENCODING).name - - -def register_surrogateescape(): - """ - Registers the surrogateescape error handler on Python 2 (only) - """ - if utils.PY3: - return - try: - codecs.lookup_error(FS_ERRORS) - except LookupError: - codecs.register_error(FS_ERRORS, surrogateescape_handler) - - -if __name__ == '__main__': - pass - # # Tests: - # register_surrogateescape() - - # b = decodefilename(fn) - # assert b == encoded, "%r != %r" % (b, encoded) - # c = encodefilename(b) - # assert c == fn, '%r != %r' % (c, fn) - # # print("ok") - - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixer_util.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixer_util.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixer_util.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixer_util.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,518 +0,0 @@ -""" -Utility functions from 2to3, 3to2 and python-modernize (and some home-grown -ones). - -Licences: -2to3: PSF License v2 -3to2: Apache Software License (from 3to2/setup.py) -python-modernize licence: BSD (from python-modernize/LICENSE) -""" - -from lib2to3.fixer_util import (FromImport, Newline, is_import, - find_root, does_tree_import, Comma) -from lib2to3.pytree import Leaf, Node -from lib2to3.pygram import python_symbols as syms, python_grammar -from lib2to3.pygram import token -from lib2to3.fixer_util import (Node, Call, Name, syms, Comma, Number) -import re - - -def canonical_fix_name(fix, avail_fixes): - """ - Examples: - >>> canonical_fix_name('fix_wrap_text_literals') - 'libfuturize.fixes.fix_wrap_text_literals' - >>> canonical_fix_name('wrap_text_literals') - 'libfuturize.fixes.fix_wrap_text_literals' - >>> canonical_fix_name('wrap_te') - ValueError("unknown fixer name") - >>> canonical_fix_name('wrap') - ValueError("ambiguous fixer name") - """ - if ".fix_" in fix: - return fix - else: - if fix.startswith('fix_'): - fix = fix[4:] - # Infer the full module name for the fixer. - # First ensure that no names clash (e.g. - # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah): - found = [f for f in avail_fixes - if f.endswith('fix_{0}'.format(fix))] - if len(found) > 1: - raise ValueError("Ambiguous fixer name. Choose a fully qualified " - "module name instead from these:\n" + - "\n".join(" " + myf for myf in found)) - elif len(found) == 0: - raise ValueError("Unknown fixer. Use --list-fixes or -l for a list.") - return found[0] - - - -## These functions are from 3to2 by Joe Amenta: - -def Star(prefix=None): - return Leaf(token.STAR, u'*', prefix=prefix) - -def DoubleStar(prefix=None): - return Leaf(token.DOUBLESTAR, u'**', prefix=prefix) - -def Minus(prefix=None): - return Leaf(token.MINUS, u'-', prefix=prefix) - -def commatize(leafs): - """ - Accepts/turns: (Name, Name, ..., Name, Name) - Returns/into: (Name, Comma, Name, Comma, ..., Name, Comma, Name) - """ - new_leafs = [] - for leaf in leafs: - new_leafs.append(leaf) - new_leafs.append(Comma()) - del new_leafs[-1] - return new_leafs - -def indentation(node): - """ - Returns the indentation for this node - Iff a node is in a suite, then it has indentation. - """ - while node.parent is not None and node.parent.type != syms.suite: - node = node.parent - if node.parent is None: - return u"" - # The first three children of a suite are NEWLINE, INDENT, (some other node) - # INDENT.value contains the indentation for this suite - # anything after (some other node) has the indentation as its prefix. - if node.type == token.INDENT: - return node.value - elif node.prev_sibling is not None and node.prev_sibling.type == token.INDENT: - return node.prev_sibling.value - elif node.prev_sibling is None: - return u"" - else: - return node.prefix - -def indentation_step(node): - """ - Dirty little trick to get the difference between each indentation level - Implemented by finding the shortest indentation string - (technically, the "least" of all of the indentation strings, but - tabs and spaces mixed won't get this far, so those are synonymous.) - """ - r = find_root(node) - # Collect all indentations into one set. - all_indents = set(i.value for i in r.pre_order() if i.type == token.INDENT) - if not all_indents: - # nothing is indented anywhere, so we get to pick what we want - return u" " # four spaces is a popular convention - else: - return min(all_indents) - -def suitify(parent): - """ - Turn the stuff after the first colon in parent's children - into a suite, if it wasn't already - """ - for node in parent.children: - if node.type == syms.suite: - # already in the prefered format, do nothing - return - - # One-liners have no suite node, we have to fake one up - for i, node in enumerate(parent.children): - if node.type == token.COLON: - break - else: - raise ValueError(u"No class suite and no ':'!") - # Move everything into a suite node - suite = Node(syms.suite, [Newline(), Leaf(token.INDENT, indentation(node) + indentation_step(node))]) - one_node = parent.children[i+1] - one_node.remove() - one_node.prefix = u'' - suite.append_child(one_node) - parent.append_child(suite) - -def NameImport(package, as_name=None, prefix=None): - """ - Accepts a package (Name node), name to import it as (string), and - optional prefix and returns a node: - import [as ] - """ - if prefix is None: - prefix = u"" - children = [Name(u"import", prefix=prefix), package] - if as_name is not None: - children.extend([Name(u"as", prefix=u" "), - Name(as_name, prefix=u" ")]) - return Node(syms.import_name, children) - -_compound_stmts = (syms.if_stmt, syms.while_stmt, syms.for_stmt, syms.try_stmt, syms.with_stmt) -_import_stmts = (syms.import_name, syms.import_from) - -def import_binding_scope(node): - """ - Generator yields all nodes for which a node (an import_stmt) has scope - The purpose of this is for a call to _find() on each of them - """ - # import_name / import_from are small_stmts - assert node.type in _import_stmts - test = node.next_sibling - # A small_stmt can only be followed by a SEMI or a NEWLINE. - while test.type == token.SEMI: - nxt = test.next_sibling - # A SEMI can only be followed by a small_stmt or a NEWLINE - if nxt.type == token.NEWLINE: - break - else: - yield nxt - # A small_stmt can only be followed by either a SEMI or a NEWLINE - test = nxt.next_sibling - # Covered all subsequent small_stmts after the import_stmt - # Now to cover all subsequent stmts after the parent simple_stmt - parent = node.parent - assert parent.type == syms.simple_stmt - test = parent.next_sibling - while test is not None: - # Yes, this will yield NEWLINE and DEDENT. Deal with it. - yield test - test = test.next_sibling - - context = parent.parent - # Recursively yield nodes following imports inside of a if/while/for/try/with statement - if context.type in _compound_stmts: - # import is in a one-liner - c = context - while c.next_sibling is not None: - yield c.next_sibling - c = c.next_sibling - context = context.parent - - # Can't chain one-liners on one line, so that takes care of that. - - p = context.parent - if p is None: - return - - # in a multi-line suite - - while p.type in _compound_stmts: - - if context.type == syms.suite: - yield context - - context = context.next_sibling - - if context is None: - context = p.parent - p = context.parent - if p is None: - break - -def ImportAsName(name, as_name, prefix=None): - new_name = Name(name) - new_as = Name(u"as", prefix=u" ") - new_as_name = Name(as_name, prefix=u" ") - new_node = Node(syms.import_as_name, [new_name, new_as, new_as_name]) - if prefix is not None: - new_node.prefix = prefix - return new_node - - -def is_docstring(node): - """ - Returns True if the node appears to be a docstring - """ - return (node.type == syms.simple_stmt and - len(node.children) > 0 and node.children[0].type == token.STRING) - - -def future_import(feature, node): - """ - This seems to work - """ - root = find_root(node) - - if does_tree_import(u"__future__", feature, node): - return - - # Look for a shebang or encoding line - shebang_encoding_idx = None - - for idx, node in enumerate(root.children): - # Is it a shebang or encoding line? - if is_shebang_comment(node) or is_encoding_comment(node): - shebang_encoding_idx = idx - if is_docstring(node): - # skip over docstring - continue - names = check_future_import(node) - if not names: - # not a future statement; need to insert before this - break - if feature in names: - # already imported - return - - import_ = FromImport(u'__future__', [Leaf(token.NAME, feature, prefix=" ")]) - if shebang_encoding_idx == 0 and idx == 0: - # If this __future__ import would go on the first line, - # detach the shebang / encoding prefix from the current first line. - # and attach it to our new __future__ import node. - import_.prefix = root.children[0].prefix - root.children[0].prefix = u'' - # End the __future__ import line with a newline and add a blank line - # afterwards: - children = [import_ , Newline()] - root.insert_child(idx, Node(syms.simple_stmt, children)) - - -def future_import2(feature, node): - """ - An alternative to future_import() which might not work ... - """ - root = find_root(node) - - if does_tree_import(u"__future__", feature, node): - return - - insert_pos = 0 - for idx, node in enumerate(root.children): - if node.type == syms.simple_stmt and node.children and \ - node.children[0].type == token.STRING: - insert_pos = idx + 1 - break - - for thing_after in root.children[insert_pos:]: - if thing_after.type == token.NEWLINE: - insert_pos += 1 - continue - - prefix = thing_after.prefix - thing_after.prefix = u"" - break - else: - prefix = u"" - - import_ = FromImport(u"__future__", [Leaf(token.NAME, feature, prefix=u" ")]) - - children = [import_, Newline()] - root.insert_child(insert_pos, Node(syms.simple_stmt, children, prefix=prefix)) - -def parse_args(arglist, scheme): - u""" - Parse a list of arguments into a dict - """ - arglist = [i for i in arglist if i.type != token.COMMA] - - ret_mapping = dict([(k, None) for k in scheme]) - - for i, arg in enumerate(arglist): - if arg.type == syms.argument and arg.children[1].type == token.EQUAL: - # argument < NAME '=' any > - slot = arg.children[0].value - ret_mapping[slot] = arg.children[2] - else: - slot = scheme[i] - ret_mapping[slot] = arg - - return ret_mapping - - -# def is_import_from(node): -# """Returns true if the node is a statement "from ... import ..." -# """ -# return node.type == syms.import_from - - -def is_import_stmt(node): - return (node.type == syms.simple_stmt and node.children and - is_import(node.children[0])) - - -def touch_import_top(package, name_to_import, node): - """Works like `does_tree_import` but adds an import statement at the - top if it was not imported (but below any __future__ imports) and below any - comments such as shebang lines). - - Based on lib2to3.fixer_util.touch_import() - - Calling this multiple times adds the imports in reverse order. - - Also adds "standard_library.install_aliases()" after "from future import - standard_library". This should probably be factored into another function. - """ - - root = find_root(node) - - if does_tree_import(package, name_to_import, root): - return - - # Ideally, we would look for whether futurize --all-imports has been run, - # as indicated by the presence of ``from builtins import (ascii, ..., - # zip)`` -- and, if it has, we wouldn't import the name again. - - # Look for __future__ imports and insert below them - found = False - for name in ['absolute_import', 'division', 'print_function', - 'unicode_literals']: - if does_tree_import('__future__', name, root): - found = True - break - if found: - # At least one __future__ import. We want to loop until we've seen them - # all. - start, end = None, None - for idx, node in enumerate(root.children): - if check_future_import(node): - start = idx - # Start looping - idx2 = start - while node: - node = node.next_sibling - idx2 += 1 - if not check_future_import(node): - end = idx2 - break - break - assert start is not None - assert end is not None - insert_pos = end - else: - # No __future__ imports. - # We look for a docstring and insert the new node below that. If no docstring - # exists, just insert the node at the top. - for idx, node in enumerate(root.children): - if node.type != syms.simple_stmt: - break - if not is_docstring(node): - # This is the usual case. - break - insert_pos = idx - - if package is None: - import_ = Node(syms.import_name, [ - Leaf(token.NAME, u"import"), - Leaf(token.NAME, name_to_import, prefix=u" ") - ]) - else: - import_ = FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")]) - if name_to_import == u'standard_library': - # Add: - # standard_library.install_aliases() - # after: - # from future import standard_library - install_hooks = Node(syms.simple_stmt, - [Node(syms.power, - [Leaf(token.NAME, u'standard_library'), - Node(syms.trailer, [Leaf(token.DOT, u'.'), - Leaf(token.NAME, u'install_aliases')]), - Node(syms.trailer, [Leaf(token.LPAR, u'('), - Leaf(token.RPAR, u')')]) - ]) - ] - ) - children_hooks = [install_hooks, Newline()] - else: - children_hooks = [] - - # FromImport(package, [Leaf(token.NAME, name_to_import, prefix=u" ")]) - - children_import = [import_, Newline()] - old_prefix = root.children[insert_pos].prefix - root.children[insert_pos].prefix = u'' - root.insert_child(insert_pos, Node(syms.simple_stmt, children_import, prefix=old_prefix)) - if len(children_hooks) > 0: - root.insert_child(insert_pos + 1, Node(syms.simple_stmt, children_hooks)) - - -## The following functions are from python-modernize by Armin Ronacher: -# (a little edited). - -def check_future_import(node): - """If this is a future import, return set of symbols that are imported, - else return None.""" - # node should be the import statement here - savenode = node - if not (node.type == syms.simple_stmt and node.children): - return set() - node = node.children[0] - # now node is the import_from node - if not (node.type == syms.import_from and - # node.type == token.NAME and # seems to break it - hasattr(node.children[1], 'value') and - node.children[1].value == u'__future__'): - return set() - node = node.children[3] - # now node is the import_as_name[s] - # print(python_grammar.number2symbol[node.type]) # breaks sometimes - if node.type == syms.import_as_names: - result = set() - for n in node.children: - if n.type == token.NAME: - result.add(n.value) - elif n.type == syms.import_as_name: - n = n.children[0] - assert n.type == token.NAME - result.add(n.value) - return result - elif node.type == syms.import_as_name: - node = node.children[0] - assert node.type == token.NAME - return set([node.value]) - elif node.type == token.NAME: - return set([node.value]) - else: - # TODO: handle brackets like this: - # from __future__ import (absolute_import, division) - assert False, "strange import: %s" % savenode - - -SHEBANG_REGEX = r'^#!.*python' -ENCODING_REGEX = r"^#.*coding[:=]\s*([-\w.]+)" - - -def is_shebang_comment(node): - """ - Comments are prefixes for Leaf nodes. Returns whether the given node has a - prefix that looks like a shebang line or an encoding line: - - #!/usr/bin/env python - #!/usr/bin/python3 - """ - return bool(re.match(SHEBANG_REGEX, node.prefix)) - - -def is_encoding_comment(node): - """ - Comments are prefixes for Leaf nodes. Returns whether the given node has a - prefix that looks like an encoding line: - - # coding: utf-8 - # encoding: utf-8 - # -*- coding: -*- - # vim: set fileencoding= : - """ - return bool(re.match(ENCODING_REGEX, node.prefix)) - - -def wrap_in_fn_call(fn_name, args, prefix=None): - """ - Example: - >>> wrap_in_fn_call("oldstr", (arg,)) - oldstr(arg) - - >>> wrap_in_fn_call("olddiv", (arg1, arg2)) - olddiv(arg1, arg2) - """ - assert len(args) > 0 - if len(args) == 1: - newargs = args - elif len(args) == 2: - expr1, expr2 = args - newargs = [expr1, Comma(), expr2] - else: - assert NotImplementedError('write me') - return Call(Name(fn_name), newargs, prefix=prefix) - - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_absolute_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -""" -Fixer for import statements, with a __future__ import line. - -Based on lib2to3/fixes/fix_import.py, but extended slightly so it also -supports Cython modules. - -If spam is being imported from the local directory, this import: - from spam import eggs -becomes: - from __future__ import absolute_import - from .spam import eggs - -and this import: - import spam -becomes: - from __future__ import absolute_import - from . import spam -""" - -from os.path import dirname, join, exists, sep -from lib2to3.fixes.fix_import import FixImport -from lib2to3.fixer_util import FromImport, syms -from lib2to3.fixes.fix_import import traverse_imports - -from libfuturize.fixer_util import future_import - - -class FixAbsoluteImport(FixImport): - run_order = 9 - - def transform(self, node, results): - """ - Copied from FixImport.transform(), but with this line added in - any modules that had implicit relative imports changed: - - from __future__ import absolute_import" - """ - if self.skip: - return - imp = results['imp'] - - if node.type == syms.import_from: - # Some imps are top-level (eg: 'import ham') - # some are first level (eg: 'import ham.eggs') - # some are third level (eg: 'import ham.eggs as spam') - # Hence, the loop - while not hasattr(imp, 'value'): - imp = imp.children[0] - if self.probably_a_local_import(imp.value): - imp.value = u"." + imp.value - imp.changed() - future_import(u"absolute_import", node) - else: - have_local = False - have_absolute = False - for mod_name in traverse_imports(imp): - if self.probably_a_local_import(mod_name): - have_local = True - else: - have_absolute = True - if have_absolute: - if have_local: - # We won't handle both sibling and absolute imports in the - # same statement at the moment. - self.warning(node, "absolute and local imports together") - return - - new = FromImport(u".", [imp]) - new.prefix = node.prefix - future_import(u"absolute_import", node) - return new - - def probably_a_local_import(self, imp_name): - """ - Like the corresponding method in the base class, but this also - supports Cython modules. - """ - if imp_name.startswith(u"."): - # Relative imports are certainly not local imports. - return False - imp_name = imp_name.split(u".", 1)[0] - base_path = dirname(self.filename) - base_path = join(base_path, imp_name) - # If there is no __init__.py next to the file its not in a package - # so can't be a relative import. - if not exists(join(dirname(base_path), "__init__.py")): - return False - for ext in [".py", sep, ".pyc", ".so", ".sl", ".pyd", ".pyx"]: - if exists(base_path + ext): - return True - return False - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_add__future__imports_except_unicode_literals.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -""" -Fixer for adding: - - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - -This is "stage 1": hopefully uncontroversial changes. - -Stage 2 adds ``unicode_literals``. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixAddFutureImportsExceptUnicodeLiterals(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - - run_order = 9 - - def transform(self, node, results): - # Reverse order: - future_import(u"print_function", node) - future_import(u"division", node) - future_import(u"absolute_import", node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_basestring.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -""" -Fixer that adds ``from past.builtins import basestring`` if there is a -reference to ``basestring`` -""" - -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -class FixBasestring(fixer_base.BaseFix): - BM_compatible = True - - PATTERN = "'basestring'" - - def transform(self, node, results): - touch_import_top(u'past.builtins', 'basestring', node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_bytes.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -"""Optional fixer that changes all unprefixed string literals "..." to b"...". - -br'abcd' is a SyntaxError on Python 2 but valid on Python 3. -ur'abcd' is a SyntaxError on Python 3 but valid on Python 2. - -""" -from __future__ import unicode_literals - -import re -from lib2to3.pgen2 import token -from lib2to3 import fixer_base - -_literal_re = re.compile(r"[^bBuUrR]?[\'\"]") - -class FixBytes(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "STRING" - - def transform(self, node, results): - if node.type == token.STRING: - if _literal_re.match(node.value): - new = node.clone() - new.value = u'b' + new.value - return new diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_cmp.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -# coding: utf-8 -""" -Fixer for the cmp() function on Py2, which was removed in Py3. - -Adds this import line:: - - from past.builtins import cmp - -if cmp() is called in the code. -""" - -from __future__ import unicode_literals -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -expression = "name='cmp'" - - -class FixCmp(fixer_base.BaseFix): - BM_compatible = True - run_order = 9 - - PATTERN = """ - power< - ({0}) trailer< '(' args=[any] ')' > - rest=any* > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'past.builtins', name.value, node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -""" -UNFINISHED -For the ``future`` package. - -Adds this import line: - - from __future__ import division - -at the top so the code runs identically on Py3 and Py2.6/2.7 -""" - -from libpasteurize.fixes.fix_division import FixDivision - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_division_safe.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line: - - from __future__ import division - -at the top and changes any old-style divisions to be calls to -past.utils.old_div so the code runs as before on Py2.6/2.7 and has the same -behaviour on Py3. - -If "from __future__ import division" is already in effect, this fixer does -nothing. -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import syms, does_tree_import -from libfuturize.fixer_util import (token, future_import, touch_import_top, - wrap_in_fn_call) - - -def match_division(node): - u""" - __future__.division redefines the meaning of a single slash for division, - so we match that and only that. - """ - slash = token.SLASH - return node.type == slash and not node.next_sibling.type == slash and \ - not node.prev_sibling.type == slash - - -class FixDivisionSafe(fixer_base.BaseFix): - # BM_compatible = True - run_order = 4 # this seems to be ignored? - - _accept_type = token.SLASH - - PATTERN = """ - term<(not('/') any)+ '/' ((not('/') any))> - """ - - def start_tree(self, tree, name): - """ - Skip this fixer if "__future__.division" is already imported. - """ - super(FixDivisionSafe, self).start_tree(tree, name) - self.skip = "division" in tree.future_features - - def match(self, node): - u""" - Since the tree needs to be fixed once and only once if and only if it - matches, we can start discarding matches after the first. - """ - if (node.type == self.syms.term and - len(node.children) == 3 and - match_division(node.children[1])): - expr1, expr2 = node.children[0], node.children[2] - return expr1, expr2 - else: - return False - - def transform(self, node, results): - if self.skip: - return - future_import(u"division", node) - - touch_import_top(u'past.utils', u'old_div', node) - expr1, expr2 = results[0].clone(), results[1].clone() - # Strip any leading space for the first number: - expr1.prefix = u'' - return wrap_in_fn_call("old_div", (expr1, expr2), prefix=node.prefix) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_execfile.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -# coding: utf-8 -""" -Fixer for the execfile() function on Py2, which was removed in Py3. - -The Lib/lib2to3/fixes/fix_execfile.py module has some problems: see -python-future issue #37. This fixer merely imports execfile() from -past.builtins and leaves the code alone. - -Adds this import line:: - - from past.builtins import execfile - -for the function execfile() that was removed from Py3. -""" - -from __future__ import unicode_literals -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -expression = "name='execfile'" - - -class FixExecfile(fixer_base.BaseFix): - BM_compatible = True - run_order = 9 - - PATTERN = """ - power< - ({0}) trailer< '(' args=[any] ')' > - rest=any* > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'past.builtins', name.value, node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line:: - - from builtins import XYZ - -for each of the functions XYZ that is used in the module. - -Adds these imports after any other imports (in an initial block of them). -""" - -from __future__ import unicode_literals - -from lib2to3 import fixer_base -from lib2to3.pygram import python_symbols as syms -from lib2to3.fixer_util import Name, Call, in_special_context - -from libfuturize.fixer_util import touch_import_top - -# All builtins are: -# from future.builtins.iterators import (filter, map, zip) -# from future.builtins.misc import (ascii, chr, hex, input, isinstance, oct, open, round, super) -# from future.types import (bytes, dict, int, range, str) -# We don't need isinstance any more. - -replaced_builtin_fns = '''filter map zip - ascii chr hex input next oct - bytes range str raw_input'''.split() - # This includes raw_input as a workaround for the - # lib2to3 fixer for raw_input on Py3 (only), allowing - # the correct import to be included. (Py3 seems to run - # the fixers the wrong way around, perhaps ignoring the - # run_order class attribute below ...) - -expression = '|'.join(["name='{0}'".format(name) for name in replaced_builtin_fns]) - - -class FixFutureBuiltins(fixer_base.BaseFix): - BM_compatible = True - run_order = 7 - - # Currently we only match uses as a function. This doesn't match e.g.: - # if isinstance(s, str): - # ... - PATTERN = """ - power< - ({0}) trailer< '(' [arglist=any] ')' > - rest=any* > - | - power< - 'map' trailer< '(' [arglist=any] ')' > - > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'builtins', name.value, node) - # name.replace(Name(u"input", prefix=name.prefix)) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -""" -For the ``future`` package. - -Changes any imports needed to reflect the standard library reorganization. Also -Also adds these import lines: - - from future import standard_library - standard_library.install_aliases() - -after any __future__ imports but before any other imports. -""" - -from lib2to3.fixes.fix_imports import FixImports -from libfuturize.fixer_util import touch_import_top - - -class FixFutureStandardLibrary(FixImports): - run_order = 8 - - def transform(self, node, results): - result = super(FixFutureStandardLibrary, self).transform(node, results) - # TODO: add a blank line between any __future__ imports and this? - touch_import_top(u'future', u'standard_library', node) - return result - - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_future_standard_library_urllib.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -""" -For the ``future`` package. - -A special fixer that ensures that these lines have been added:: - - from future import standard_library - standard_library.install_hooks() - -even if the only module imported was ``urllib``, in which case the regular fixer -wouldn't have added these lines. - -""" - -from lib2to3.fixes.fix_urllib import FixUrllib -from libfuturize.fixer_util import touch_import_top, find_root - - -class FixFutureStandardLibraryUrllib(FixUrllib): # not a subclass of FixImports - run_order = 8 - - def transform(self, node, results): - # transform_member() in lib2to3/fixes/fix_urllib.py breaks node so find_root(node) - # no longer works after the super() call below. So we find the root first: - root = find_root(node) - result = super(FixFutureStandardLibraryUrllib, self).transform(node, results) - # TODO: add a blank line between any __future__ imports and this? - touch_import_top(u'future', u'standard_library', root) - return result - - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_metaclass.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,262 +0,0 @@ -# coding: utf-8 -"""Fixer for __metaclass__ = X -> (future.utils.with_metaclass(X)) methods. - - The various forms of classef (inherits nothing, inherits once, inherints - many) don't parse the same in the CST so we look at ALL classes for - a __metaclass__ and if we find one normalize the inherits to all be - an arglist. - - For one-liner classes ('class X: pass') there is no indent/dedent so - we normalize those into having a suite. - - Moving the __metaclass__ into the classdef can also cause the class - body to be empty so there is some special casing for that as well. - - This fixer also tries very hard to keep original indenting and spacing - in all those corner cases. -""" -# This is a derived work of Lib/lib2to3/fixes/fix_metaclass.py under the -# copyright of the Python Software Foundation, licensed under the Python -# Software Foundation License 2. -# -# Copyright notice: -# -# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -# 2011, 2012, 2013 Python Software Foundation. All rights reserved. -# -# Full license text: http://docs.python.org/3.4/license.html - -# Author: Jack Diederich, Daniel Neuhäuser - -# Local imports -from lib2to3 import fixer_base -from lib2to3.pygram import token -from lib2to3.fixer_util import Name, syms, Node, Leaf, touch_import, Call, \ - String, Comma, parenthesize - - -def has_metaclass(parent): - """ we have to check the cls_node without changing it. - There are two possiblities: - 1) clsdef => suite => simple_stmt => expr_stmt => Leaf('__meta') - 2) clsdef => simple_stmt => expr_stmt => Leaf('__meta') - """ - for node in parent.children: - if node.type == syms.suite: - return has_metaclass(node) - elif node.type == syms.simple_stmt and node.children: - expr_node = node.children[0] - if expr_node.type == syms.expr_stmt and expr_node.children: - left_side = expr_node.children[0] - if isinstance(left_side, Leaf) and \ - left_side.value == '__metaclass__': - return True - return False - - -def fixup_parse_tree(cls_node): - """ one-line classes don't get a suite in the parse tree so we add - one to normalize the tree - """ - for node in cls_node.children: - if node.type == syms.suite: - # already in the preferred format, do nothing - return - - # !%@#! oneliners have no suite node, we have to fake one up - for i, node in enumerate(cls_node.children): - if node.type == token.COLON: - break - else: - raise ValueError("No class suite and no ':'!") - - # move everything into a suite node - suite = Node(syms.suite, []) - while cls_node.children[i+1:]: - move_node = cls_node.children[i+1] - suite.append_child(move_node.clone()) - move_node.remove() - cls_node.append_child(suite) - node = suite - - -def fixup_simple_stmt(parent, i, stmt_node): - """ if there is a semi-colon all the parts count as part of the same - simple_stmt. We just want the __metaclass__ part so we move - everything efter the semi-colon into its own simple_stmt node - """ - for semi_ind, node in enumerate(stmt_node.children): - if node.type == token.SEMI: # *sigh* - break - else: - return - - node.remove() # kill the semicolon - new_expr = Node(syms.expr_stmt, []) - new_stmt = Node(syms.simple_stmt, [new_expr]) - while stmt_node.children[semi_ind:]: - move_node = stmt_node.children[semi_ind] - new_expr.append_child(move_node.clone()) - move_node.remove() - parent.insert_child(i, new_stmt) - new_leaf1 = new_stmt.children[0].children[0] - old_leaf1 = stmt_node.children[0].children[0] - new_leaf1.prefix = old_leaf1.prefix - - -def remove_trailing_newline(node): - if node.children and node.children[-1].type == token.NEWLINE: - node.children[-1].remove() - - -def find_metas(cls_node): - # find the suite node (Mmm, sweet nodes) - for node in cls_node.children: - if node.type == syms.suite: - break - else: - raise ValueError("No class suite!") - - # look for simple_stmt[ expr_stmt[ Leaf('__metaclass__') ] ] - for i, simple_node in list(enumerate(node.children)): - if simple_node.type == syms.simple_stmt and simple_node.children: - expr_node = simple_node.children[0] - if expr_node.type == syms.expr_stmt and expr_node.children: - # Check if the expr_node is a simple assignment. - left_node = expr_node.children[0] - if isinstance(left_node, Leaf) and \ - left_node.value == u'__metaclass__': - # We found a assignment to __metaclass__. - fixup_simple_stmt(node, i, simple_node) - remove_trailing_newline(simple_node) - yield (node, i, simple_node) - - -def fixup_indent(suite): - """ If an INDENT is followed by a thing with a prefix then nuke the prefix - Otherwise we get in trouble when removing __metaclass__ at suite start - """ - kids = suite.children[::-1] - # find the first indent - while kids: - node = kids.pop() - if node.type == token.INDENT: - break - - # find the first Leaf - while kids: - node = kids.pop() - if isinstance(node, Leaf) and node.type != token.DEDENT: - if node.prefix: - node.prefix = u'' - return - else: - kids.extend(node.children[::-1]) - - -class FixMetaclass(fixer_base.BaseFix): - BM_compatible = True - - PATTERN = """ - classdef - """ - - def transform(self, node, results): - if not has_metaclass(node): - return - - fixup_parse_tree(node) - - # find metaclasses, keep the last one - last_metaclass = None - for suite, i, stmt in find_metas(node): - last_metaclass = stmt - stmt.remove() - - text_type = node.children[0].type # always Leaf(nnn, 'class') - - # figure out what kind of classdef we have - if len(node.children) == 7: - # Node(classdef, ['class', 'name', '(', arglist, ')', ':', suite]) - # 0 1 2 3 4 5 6 - if node.children[3].type == syms.arglist: - arglist = node.children[3] - # Node(classdef, ['class', 'name', '(', 'Parent', ')', ':', suite]) - else: - parent = node.children[3].clone() - arglist = Node(syms.arglist, [parent]) - node.set_child(3, arglist) - elif len(node.children) == 6: - # Node(classdef, ['class', 'name', '(', ')', ':', suite]) - # 0 1 2 3 4 5 - arglist = Node(syms.arglist, []) - node.insert_child(3, arglist) - elif len(node.children) == 4: - # Node(classdef, ['class', 'name', ':', suite]) - # 0 1 2 3 - arglist = Node(syms.arglist, []) - node.insert_child(2, Leaf(token.RPAR, u')')) - node.insert_child(2, arglist) - node.insert_child(2, Leaf(token.LPAR, u'(')) - else: - raise ValueError("Unexpected class definition") - - # now stick the metaclass in the arglist - meta_txt = last_metaclass.children[0].children[0] - meta_txt.value = 'metaclass' - orig_meta_prefix = meta_txt.prefix - - # Was: touch_import(None, u'future.utils', node) - touch_import(u'future.utils', u'with_metaclass', node) - - metaclass = last_metaclass.children[0].children[2].clone() - metaclass.prefix = u'' - - arguments = [metaclass] - - if arglist.children: - if len(arglist.children) == 1: - base = arglist.children[0].clone() - base.prefix = u' ' - else: - # Unfortunately six.with_metaclass() only allows one base - # class, so we have to dynamically generate a base class if - # there is more than one. - bases = parenthesize(arglist.clone()) - bases.prefix = u' ' - base = Call(Name('type'), [ - String("'NewBase'"), - Comma(), - bases, - Comma(), - Node( - syms.atom, - [Leaf(token.LBRACE, u'{'), Leaf(token.RBRACE, u'}')], - prefix=u' ' - ) - ], prefix=u' ') - arguments.extend([Comma(), base]) - - arglist.replace(Call( - Name(u'with_metaclass', prefix=arglist.prefix), - arguments - )) - - fixup_indent(suite) - - # check for empty suite - if not suite.children: - # one-liner that was just __metaclass_ - suite.remove() - pass_leaf = Leaf(text_type, u'pass') - pass_leaf.prefix = orig_meta_prefix - node.append_child(pass_leaf) - node.append_child(Leaf(token.NEWLINE, u'\n')) - - elif len(suite.children) > 1 and \ - (suite.children[-2].type == token.INDENT and - suite.children[-1].type == token.DEDENT): - # there was only one line in the class body and it was __metaclass__ - pass_leaf = Leaf(text_type, u'pass') - suite.insert_child(-1, pass_leaf) - suite.insert_child(-1, Leaf(token.NEWLINE, u'\n')) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_next_call.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -""" -Based on fix_next.py by Collin Winter. - -Replaces it.next() -> next(it), per PEP 3114. - -Unlike fix_next.py, this fixer doesn't replace the name of a next method with __next__, -which would break Python 2 compatibility without further help from fixers in -stage 2. -""" - -# Local imports -from lib2to3.pgen2 import token -from lib2to3.pygram import python_symbols as syms -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, Call, find_binding - -bind_warning = "Calls to builtin next() possibly shadowed by global binding" - - -class FixNextCall(fixer_base.BaseFix): - BM_compatible = True - PATTERN = """ - power< base=any+ trailer< '.' attr='next' > trailer< '(' ')' > > - | - power< head=any+ trailer< '.' attr='next' > not trailer< '(' ')' > > - | - global=global_stmt< 'global' any* 'next' any* > - """ - - order = "pre" # Pre-order tree traversal - - def start_tree(self, tree, filename): - super(FixNextCall, self).start_tree(tree, filename) - - n = find_binding('next', tree) - if n: - self.warning(n, bind_warning) - self.shadowed_next = True - else: - self.shadowed_next = False - - def transform(self, node, results): - assert results - - base = results.get("base") - attr = results.get("attr") - name = results.get("name") - - if base: - if self.shadowed_next: - # Omit this: - # attr.replace(Name("__next__", prefix=attr.prefix)) - pass - else: - base = [n.clone() for n in base] - base[0].prefix = "" - node.replace(Call(Name("next", prefix=node.prefix), base)) - elif name: - # Omit this: - # n = Name("__next__", prefix=name.prefix) - # name.replace(n) - pass - elif attr: - # We don't do this transformation if we're assigning to "x.next". - # Unfortunately, it doesn't seem possible to do this in PATTERN, - # so it's being done here. - if is_assign_target(node): - head = results["head"] - if "".join([str(n) for n in head]).strip() == '__builtin__': - self.warning(node, bind_warning) - return - # Omit this: - # attr.replace(Name("__next__")) - elif "global" in results: - self.warning(node, bind_warning) - self.shadowed_next = True - - -### The following functions help test if node is part of an assignment -### target. - -def is_assign_target(node): - assign = find_assign(node) - if assign is None: - return False - - for child in assign.children: - if child.type == token.EQUAL: - return False - elif is_subtree(child, node): - return True - return False - -def find_assign(node): - if node.type == syms.expr_stmt: - return node - if node.type == syms.simple_stmt or node.parent is None: - return None - return find_assign(node.parent) - -def is_subtree(root, node): - if root == node: - return True - return any(is_subtree(c, node) for c in root.children) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_object.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_object.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_object.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_object.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -""" -Fixer that adds ``from builtins import object`` if there is a line -like this: - class Foo(object): -""" - -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -class FixObject(fixer_base.BaseFix): - - PATTERN = u"classdef< 'class' NAME '(' name='object' ')' colon=':' any >" - - def transform(self, node, results): - touch_import_top(u'builtins', 'object', node) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_oldstr_wrap.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line: - - from past.builtins import str as oldstr - -at the top and wraps any unadorned string literals 'abc' or explicit byte-string -literals b'abc' in oldstr() calls so the code has the same behaviour on Py3 as -on Py2.6/2.7. -""" - -from __future__ import unicode_literals -import re -from lib2to3 import fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import syms -from libfuturize.fixer_util import (future_import, touch_import_top, - wrap_in_fn_call) - - -_literal_re = re.compile(r"[^uUrR]?[\'\"]") - - -class FixOldstrWrap(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "STRING" - - def transform(self, node, results): - if node.type == token.STRING: - touch_import_top(u'past.types', u'oldstr', node) - if _literal_re.match(node.value): - new = node.clone() - # Strip any leading space or comments: - # TODO: check: do we really want to do this? - new.prefix = u'' - new.value = u'b' + new.value - wrapped = wrap_in_fn_call("oldstr", [new], prefix=node.prefix) - return wrapped diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_order___future__imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -""" -UNFINISHED - -Fixer for turning multiple lines like these: - - from __future__ import division - from __future__ import absolute_import - from __future__ import print_function - -into a single line like this: - - from __future__ import (absolute_import, division, print_function) - -This helps with testing of ``futurize``. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixOrderFutureImports(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - - run_order = 10 - - # def match(self, node): - # """ - # Match only once per file - # """ - # if hasattr(node, 'type') and node.type == syms.file_input: - # return True - # return False - - def transform(self, node, results): - # TODO # write me - pass - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -# Copyright 2006 Google, Inc. All Rights Reserved. -# Licensed to PSF under a Contributor Agreement. - -"""Fixer for print. - -Change: - "print" into "print()" - "print ..." into "print(...)" - "print(...)" not changed - "print ... ," into "print(..., end=' ')" - "print >>x, ..." into "print(..., file=x)" - -No changes are applied if print_function is imported from __future__ - -""" - -# Local imports -from lib2to3 import patcomp, pytree, fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import Name, Call, Comma, String -# from libmodernize import add_future - -parend_expr = patcomp.compile_pattern( - """atom< '(' [arith_expr|atom|power|term|STRING|NAME] ')' >""" - ) - - -class FixPrint(fixer_base.BaseFix): - - BM_compatible = True - - PATTERN = """ - simple_stmt< any* bare='print' any* > | print_stmt - """ - - def transform(self, node, results): - assert results - - bare_print = results.get("bare") - - if bare_print: - # Special-case print all by itself. - bare_print.replace(Call(Name(u"print"), [], - prefix=bare_print.prefix)) - # The "from __future__ import print_function"" declaration is added - # by the fix_print_with_import fixer, so we skip it here. - # add_future(node, u'print_function') - return - assert node.children[0] == Name(u"print") - args = node.children[1:] - if len(args) == 1 and parend_expr.match(args[0]): - # We don't want to keep sticking parens around an - # already-parenthesised expression. - return - - sep = end = file = None - if args and args[-1] == Comma(): - args = args[:-1] - end = " " - if args and args[0] == pytree.Leaf(token.RIGHTSHIFT, u">>"): - assert len(args) >= 2 - file = args[1].clone() - args = args[3:] # Strip a possible comma after the file expression - # Now synthesize a print(args, sep=..., end=..., file=...) node. - l_args = [arg.clone() for arg in args] - if l_args: - l_args[0].prefix = u"" - if sep is not None or end is not None or file is not None: - if sep is not None: - self.add_kwarg(l_args, u"sep", String(repr(sep))) - if end is not None: - self.add_kwarg(l_args, u"end", String(repr(end))) - if file is not None: - self.add_kwarg(l_args, u"file", file) - n_stmt = Call(Name(u"print"), l_args) - n_stmt.prefix = node.prefix - - # Note that there are corner cases where adding this future-import is - # incorrect, for example when the file also has a 'print ()' statement - # that was intended to print "()". - # add_future(node, u'print_function') - return n_stmt - - def add_kwarg(self, l_nodes, s_kwd, n_expr): - # XXX All this prefix-setting may lose comments (though rarely) - n_expr.prefix = u"" - n_argument = pytree.Node(self.syms.argument, - (Name(s_kwd), - pytree.Leaf(token.EQUAL, u"="), - n_expr)) - if l_nodes: - l_nodes.append(Comma()) - n_argument.prefix = u" " - l_nodes.append(n_argument) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_print_with_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -""" -For the ``future`` package. - -Turns any print statements into functions and adds this import line: - - from __future__ import print_function - -at the top to retain compatibility with Python 2.6+. -""" - -from libfuturize.fixes.fix_print import FixPrint -from libfuturize.fixer_util import future_import - -class FixPrintWithImport(FixPrint): - run_order = 7 - def transform(self, node, results): - # Add the __future__ import first. (Otherwise any shebang or encoding - # comment line attached as a prefix to the print statement will be - # copied twice and appear twice.) - future_import(u'print_function', node) - n_stmt = super(FixPrintWithImport, self).transform(node, results) - return n_stmt - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_raise.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -"""Fixer for 'raise E, V' - -From Armin Ronacher's ``python-modernize``. - -raise -> raise -raise E -> raise E -raise E, V -> raise E(V) - -raise (((E, E'), E''), E'''), V -> raise E(V) - - -CAVEATS: -1) "raise E, V" will be incorrectly translated if V is an exception - instance. The correct Python 3 idiom is - - raise E from V - - but since we can't detect instance-hood by syntax alone and since - any client code would have to be changed as well, we don't automate - this. -""" -# Author: Collin Winter, Armin Ronacher - -# Local imports -from lib2to3 import pytree, fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import Name, Call, is_tuple - -class FixRaise(fixer_base.BaseFix): - - BM_compatible = True - PATTERN = """ - raise_stmt< 'raise' exc=any [',' val=any] > - """ - - def transform(self, node, results): - syms = self.syms - - exc = results["exc"].clone() - if exc.type == token.STRING: - msg = "Python 3 does not support string exceptions" - self.cannot_convert(node, msg) - return - - # Python 2 supports - # raise ((((E1, E2), E3), E4), E5), V - # as a synonym for - # raise E1, V - # Since Python 3 will not support this, we recurse down any tuple - # literals, always taking the first element. - if is_tuple(exc): - while is_tuple(exc): - # exc.children[1:-1] is the unparenthesized tuple - # exc.children[1].children[0] is the first element of the tuple - exc = exc.children[1].children[0].clone() - exc.prefix = u" " - - if "val" not in results: - # One-argument raise - new = pytree.Node(syms.raise_stmt, [Name(u"raise"), exc]) - new.prefix = node.prefix - return new - - val = results["val"].clone() - if is_tuple(val): - args = [c.clone() for c in val.children[1:-1]] - else: - val.prefix = u"" - args = [val] - - return pytree.Node(syms.raise_stmt, - [Name(u"raise"), Call(exc, args)], - prefix=node.prefix) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_remove_old__future__imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -""" -Fixer for removing any of these lines: - - from __future__ import with_statement - from __future__ import nested_scopes - from __future__ import generators - -The reason is that __future__ imports like these are required to be the first -line of code (after docstrings) on Python 2.6+, which can get in the way. - -These imports are always enabled in Python 2.6+, which is the minimum sane -version to target for Py2/3 compatibility. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import remove_future_import - -class FixRemoveOldFutureImports(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 1 - - def transform(self, node, results): - remove_future_import(u"with_statement", node) - remove_future_import(u"nested_scopes", node) - remove_future_import(u"generators", node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_keep_u.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -"""Fixer that changes unicode to str and unichr to chr, but -- unlike the -lib2to3 fix_unicode.py fixer, does not change u"..." into "...". - -The reason is that Py3.3+ supports the u"..." string prefix, and, if -present, the prefix may provide useful information for disambiguating -between byte strings and unicode strings, which is often the hardest part -of the porting task. - -""" - -from lib2to3.pgen2 import token -from lib2to3 import fixer_base - -_mapping = {u"unichr" : u"chr", u"unicode" : u"str"} - -class FixUnicodeKeepU(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "'unicode' | 'unichr'" - - def transform(self, node, results): - if node.type == token.NAME: - new = node.clone() - new.value = _mapping[node.value] - return new - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_unicode_literals_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -""" -Adds this import: - - from __future__ import unicode_literals - -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixUnicodeLiteralsImport(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - - run_order = 9 - - def transform(self, node, results): - future_import(u"unicode_literals", node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_UserDict.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -"""Fix UserDict. - -Incomplete! - -TODO: base this on fix_urllib perhaps? -""" - - -# Local imports -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, attr_chain -from lib2to3.fixes.fix_imports import alternates, build_pattern, FixImports - -MAPPING = {'UserDict': 'collections', -} - -# def alternates(members): -# return "(" + "|".join(map(repr, members)) + ")" -# -# -# def build_pattern(mapping=MAPPING): -# mod_list = ' | '.join(["module_name='%s'" % key for key in mapping]) -# bare_names = alternates(mapping.keys()) -# -# yield """name_import=import_name< 'import' ((%s) | -# multiple_imports=dotted_as_names< any* (%s) any* >) > -# """ % (mod_list, mod_list) -# yield """import_from< 'from' (%s) 'import' ['('] -# ( any | import_as_name< any 'as' any > | -# import_as_names< any* >) [')'] > -# """ % mod_list -# yield """import_name< 'import' (dotted_as_name< (%s) 'as' any > | -# multiple_imports=dotted_as_names< -# any* dotted_as_name< (%s) 'as' any > any* >) > -# """ % (mod_list, mod_list) -# -# # Find usages of module members in code e.g. thread.foo(bar) -# yield "power< bare_with_attr=(%s) trailer<'.' any > any* >" % bare_names - - -# class FixUserDict(fixer_base.BaseFix): -class FixUserdict(FixImports): - - BM_compatible = True - keep_line_order = True - # This is overridden in fix_imports2. - mapping = MAPPING - - # We want to run this fixer late, so fix_import doesn't try to make stdlib - # renames into relative imports. - run_order = 6 - - def build_pattern(self): - return "|".join(build_pattern(self.mapping)) - - def compile_pattern(self): - # We override this, so MAPPING can be pragmatically altered and the - # changes will be reflected in PATTERN. - self.PATTERN = self.build_pattern() - super(FixImports, self).compile_pattern() - - # Don't match the node if it's within another match. - def match(self, node): - match = super(FixImports, self).match - results = match(node) - if results: - # Module usage could be in the trailer of an attribute lookup, so we - # might have nested matches when "bare_with_attr" is present. - if "bare_with_attr" not in results and \ - any(match(obj) for obj in attr_chain(node, "parent")): - return False - return results - return False - - def start_tree(self, tree, filename): - super(FixImports, self).start_tree(tree, filename) - self.replace = {} - - def transform(self, node, results): - import_mod = results.get("module_name") - if import_mod: - mod_name = import_mod.value - new_name = unicode(self.mapping[mod_name]) - import_mod.replace(Name(new_name, prefix=import_mod.prefix)) - if "name_import" in results: - # If it's not a "from x import x, y" or "import x as y" import, - # marked its usage to be replaced. - self.replace[mod_name] = new_name - if "multiple_imports" in results: - # This is a nasty hack to fix multiple imports on a line (e.g., - # "import StringIO, urlparse"). The problem is that I can't - # figure out an easy way to make a pattern recognize the keys of - # MAPPING randomly sprinkled in an import statement. - results = self.match(node) - if results: - self.transform(node, results) - else: - # Replace usage of the module. - bare_name = results["bare_with_attr"][0] - new_name = self.replace.get(bare_name.value) - if new_name: - bare_name.replace(Name(new_name, prefix=bare_name.prefix)) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/fix_xrange_with_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -""" -For the ``future`` package. - -Turns any xrange calls into range calls and adds this import line: - - from builtins import range - -at the top. -""" - -from lib2to3.fixes.fix_xrange import FixXrange - -from libfuturize.fixer_util import touch_import_top - - -class FixXrangeWithImport(FixXrange): - def transform(self, node, results): - result = super(FixXrangeWithImport, self).transform(node, results) - touch_import_top('builtins', 'range', node) - return result diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/fixes/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/fixes/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -import sys -from lib2to3 import refactor - -# The following fixers are "safe": they convert Python 2 code to more -# modern Python 2 code. They should be uncontroversial to apply to most -# projects that are happy to drop support for Py2.5 and below. Applying -# them first will reduce the size of the patch set for the real porting. -lib2to3_fix_names_stage1 = set([ - 'lib2to3.fixes.fix_apply', - 'lib2to3.fixes.fix_except', - 'lib2to3.fixes.fix_exec', - 'lib2to3.fixes.fix_exitfunc', - 'lib2to3.fixes.fix_funcattrs', - 'lib2to3.fixes.fix_has_key', - 'lib2to3.fixes.fix_idioms', - # 'lib2to3.fixes.fix_import', # makes any implicit relative imports explicit. (Use with ``from __future__ import absolute_import) - 'lib2to3.fixes.fix_intern', - 'lib2to3.fixes.fix_isinstance', - 'lib2to3.fixes.fix_methodattrs', - 'lib2to3.fixes.fix_ne', - # 'lib2to3.fixes.fix_next', # would replace ``next`` method names - # with ``__next__``. - 'lib2to3.fixes.fix_numliterals', # turns 1L into 1, 0755 into 0o755 - 'lib2to3.fixes.fix_paren', - # 'lib2to3.fixes.fix_print', # see the libfuturize fixer that also - # adds ``from __future__ import print_function`` - # 'lib2to3.fixes.fix_raise', # uses incompatible with_traceback() method on exceptions - 'lib2to3.fixes.fix_reduce', # reduce is available in functools on Py2.6/Py2.7 - 'lib2to3.fixes.fix_renames', # sys.maxint -> sys.maxsize - # 'lib2to3.fixes.fix_set_literal', # this is unnecessary and breaks Py2.6 support - 'lib2to3.fixes.fix_repr', - 'lib2to3.fixes.fix_standarderror', - 'lib2to3.fixes.fix_sys_exc', - 'lib2to3.fixes.fix_throw', - 'lib2to3.fixes.fix_tuple_params', - 'lib2to3.fixes.fix_types', - 'lib2to3.fixes.fix_ws_comma', # can perhaps decrease readability: see issue #58 - 'lib2to3.fixes.fix_xreadlines', -]) - -# The following fixers add a dependency on the ``future`` package on order to -# support Python 2: -lib2to3_fix_names_stage2 = set([ - # 'lib2to3.fixes.fix_buffer', # perhaps not safe. Test this. - # 'lib2to3.fixes.fix_callable', # not needed in Py3.2+ - 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. and move to stage2 - # 'lib2to3.fixes.fix_execfile', # some problems: see issue #37. - # We use a custom fixer instead (see below) - # 'lib2to3.fixes.fix_future', # we don't want to remove __future__ imports - 'lib2to3.fixes.fix_getcwdu', - # 'lib2to3.fixes.fix_imports', # called by libfuturize.fixes.fix_future_standard_library - # 'lib2to3.fixes.fix_imports2', # we don't handle this yet (dbm) - 'lib2to3.fixes.fix_input', - 'lib2to3.fixes.fix_itertools', - 'lib2to3.fixes.fix_itertools_imports', - 'lib2to3.fixes.fix_filter', - 'lib2to3.fixes.fix_long', - 'lib2to3.fixes.fix_map', - # 'lib2to3.fixes.fix_metaclass', # causes SyntaxError in Py2! Use the one from ``six`` instead - 'lib2to3.fixes.fix_next', - 'lib2to3.fixes.fix_nonzero', # TODO: cause this to import ``object`` and/or add a decorator for mapping __bool__ to __nonzero__ - 'lib2to3.fixes.fix_operator', # we will need support for this by e.g. extending the Py2 operator module to provide those functions in Py3 - 'lib2to3.fixes.fix_raw_input', - # 'lib2to3.fixes.fix_unicode', # strips off the u'' prefix, which removes a potentially helpful source of information for disambiguating unicode/byte strings - # 'lib2to3.fixes.fix_urllib', # included in libfuturize.fix_future_standard_library_urllib - # 'lib2to3.fixes.fix_xrange', # custom one because of a bug with Py3.3's lib2to3 - 'lib2to3.fixes.fix_zip', -]) - -libfuturize_fix_names_stage1 = set([ - 'libfuturize.fixes.fix_absolute_import', - 'libfuturize.fixes.fix_next_call', # obj.next() -> next(obj). Unlike - # lib2to3.fixes.fix_next, doesn't change - # the ``next`` method to ``__next__``. - 'libfuturize.fixes.fix_print_with_import', - 'libfuturize.fixes.fix_raise', - # 'libfuturize.fixes.fix_order___future__imports', # TODO: consolidate to a single line to simplify testing -]) - -libfuturize_fix_names_stage2 = set([ - 'libfuturize.fixes.fix_basestring', - # 'libfuturize.fixes.fix_add__future__imports_except_unicode_literals', # just in case - 'libfuturize.fixes.fix_cmp', - 'libfuturize.fixes.fix_division_safe', - 'libfuturize.fixes.fix_execfile', - 'libfuturize.fixes.fix_future_builtins', - 'libfuturize.fixes.fix_future_standard_library', - 'libfuturize.fixes.fix_future_standard_library_urllib', - 'libfuturize.fixes.fix_metaclass', - 'libpasteurize.fixes.fix_newstyle', - 'libfuturize.fixes.fix_object', - # 'libfuturize.fixes.fix_order___future__imports', # TODO: consolidate to a single line to simplify testing - 'libfuturize.fixes.fix_unicode_keep_u', - # 'libfuturize.fixes.fix_unicode_literals_import', - 'libfuturize.fixes.fix_xrange_with_import', # custom one because of a bug with Py3.3's lib2to3 -]) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -# empty to make this a package diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/main.py pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/main.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libfuturize/main.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libfuturize/main.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,322 +0,0 @@ -""" -futurize: automatic conversion to clean 2/3 code using ``python-future`` -====================================================================== - -Like Armin Ronacher's modernize.py, ``futurize`` attempts to produce clean -standard Python 3 code that runs on both Py2 and Py3. - -One pass --------- - -Use it like this on Python 2 code: - - $ futurize --verbose mypython2script.py - -This will attempt to port the code to standard Py3 code that also -provides Py2 compatibility with the help of the right imports from -``future``. - -To write changes to the files, use the -w flag. - -Two stages ----------- - -The ``futurize`` script can also be called in two separate stages. First: - - $ futurize --stage1 mypython2script.py - -This produces more modern Python 2 code that is not yet compatible with Python -3. The tests should still run and the diff should be uncontroversial to apply to -most Python projects that are willing to drop support for Python 2.5 and lower. - -After this, the recommended approach is to explicitly mark all strings that must -be byte-strings with a b'' prefix and all text (unicode) strings with a u'' -prefix, and then invoke the second stage of Python 2 to 2/3 conversion with:: - - $ futurize --stage2 mypython2script.py - -Stage 2 adds a dependency on ``future``. It converts most remaining Python -2-specific code to Python 3 code and adds appropriate imports from ``future`` -to restore Py2 support. - -The command above leaves all unadorned string literals as native strings -(byte-strings on Py2, unicode strings on Py3). If instead you would like all -unadorned string literals to be promoted to unicode, you can also pass this -flag: - - $ futurize --stage2 --unicode-literals mypython2script.py - -This adds the declaration ``from __future__ import unicode_literals`` to the -top of each file, which implicitly declares all unadorned string literals to be -unicode strings (``unicode`` on Py2). - -All imports ------------ - -The --all-imports option forces adding all ``__future__`` imports, -``builtins`` imports, and standard library aliases, even if they don't -seem necessary for the current state of each module. (This can simplify -testing, and can reduce the need to think about Py2 compatibility when editing -the code further.) - -""" - -from __future__ import (absolute_import, print_function, unicode_literals) -import future.utils -from future import __version__ - -import sys -import logging -import optparse -import os - -from lib2to3.main import main, warn, StdoutRefactoringTool -from lib2to3 import refactor - -from libfuturize.fixes import (lib2to3_fix_names_stage1, - lib2to3_fix_names_stage2, - libfuturize_fix_names_stage1, - libfuturize_fix_names_stage2) - -fixer_pkg = 'libfuturize.fixes' - - -def main(args=None): - """Main program. - - Args: - fixer_pkg: the name of a package where the fixers are located. - args: optional; a list of command line arguments. If omitted, - sys.argv[1:] is used. - - Returns a suggested exit status (0, 1, 2). - """ - - # Set up option parser - parser = optparse.OptionParser(usage="futurize [options] file|dir ...") - parser.add_option("-V", "--version", action="store_true", - help="Report the version number of futurize") - parser.add_option("-a", "--all-imports", action="store_true", - help="Add all __future__ and future imports to each module") - parser.add_option("-1", "--stage1", action="store_true", - help="Modernize Python 2 code only; no compatibility with Python 3 (or dependency on ``future``)") - parser.add_option("-2", "--stage2", action="store_true", - help="Take modernized (stage1) code and add a dependency on ``future`` to provide Py3 compatibility.") - parser.add_option("-0", "--both-stages", action="store_true", - help="Apply both stages 1 and 2") - parser.add_option("-u", "--unicode-literals", action="store_true", - help="Add ``from __future__ import unicode_literals`` to implicitly convert all unadorned string literals '' into unicode strings") - parser.add_option("-f", "--fix", action="append", default=[], - help="Each FIX specifies a transformation; default: all.\nEither use '-f division -f metaclass' etc. or use the fully-qualified module name: '-f lib2to3.fixes.fix_types -f libfuturize.fixes.fix_unicode_keep_u'") - parser.add_option("-j", "--processes", action="store", default=1, - type="int", help="Run 2to3 concurrently") - parser.add_option("-x", "--nofix", action="append", default=[], - help="Prevent a fixer from being run.") - parser.add_option("-l", "--list-fixes", action="store_true", - help="List available transformations") - parser.add_option("-p", "--print-function", action="store_true", - help="Modify the grammar so that print() is a function") - parser.add_option("-v", "--verbose", action="store_true", - help="More verbose logging") - parser.add_option("--no-diffs", action="store_true", - help="Don't show diffs of the refactoring") - parser.add_option("-w", "--write", action="store_true", - help="Write back modified files") - parser.add_option("-n", "--nobackups", action="store_true", default=False, - help="Don't write backups for modified files.") - parser.add_option("-o", "--output-dir", action="store", type="str", - default="", help="Put output files in this directory " - "instead of overwriting the input files. Requires -n. " - "For Python >= 2.7 only.") - parser.add_option("-W", "--write-unchanged-files", action="store_true", - help="Also write files even if no changes were required" - " (useful with --output-dir); implies -w.") - parser.add_option("--add-suffix", action="store", type="str", default="", - help="Append this string to all output filenames." - " Requires -n if non-empty. For Python >= 2.7 only." - "ex: --add-suffix='3' will generate .py3 files.") - - # Parse command line arguments - flags = {} - refactor_stdin = False - options, args = parser.parse_args(args) - - if options.write_unchanged_files: - flags["write_unchanged_files"] = True - if not options.write: - warn("--write-unchanged-files/-W implies -w.") - options.write = True - # If we allowed these, the original files would be renamed to backup names - # but not replaced. - if options.output_dir and not options.nobackups: - parser.error("Can't use --output-dir/-o without -n.") - if options.add_suffix and not options.nobackups: - parser.error("Can't use --add-suffix without -n.") - - if not options.write and options.no_diffs: - warn("not writing files and not printing diffs; that's not very useful") - if not options.write and options.nobackups: - parser.error("Can't use -n without -w") - if "-" in args: - refactor_stdin = True - if options.write: - print("Can't write to stdin.", file=sys.stderr) - return 2 - # Is this ever necessary? - if options.print_function: - flags["print_function"] = True - - # Set up logging handler - level = logging.DEBUG if options.verbose else logging.INFO - logging.basicConfig(format='%(name)s: %(message)s', level=level) - logger = logging.getLogger('libfuturize.main') - - if options.stage1 or options.stage2: - assert options.both_stages is None - options.both_stages = False - else: - options.both_stages = True - - avail_fixes = set() - - if options.stage1 or options.both_stages: - avail_fixes.update(lib2to3_fix_names_stage1) - avail_fixes.update(libfuturize_fix_names_stage1) - if options.stage2 or options.both_stages: - avail_fixes.update(lib2to3_fix_names_stage2) - avail_fixes.update(libfuturize_fix_names_stage2) - - if options.unicode_literals: - avail_fixes.add('libfuturize.fixes.fix_unicode_literals_import') - - if options.version: - print(__version__) - return 0 - if options.list_fixes: - print("Available transformations for the -f/--fix option:") - # for fixname in sorted(refactor.get_all_fix_names(fixer_pkg)): - for fixname in sorted(avail_fixes): - print(fixname) - if not args: - return 0 - if not args: - print("At least one file or directory argument required.", - file=sys.stderr) - print("Use --help to show usage.", file=sys.stderr) - return 2 - - unwanted_fixes = set() - for fix in options.nofix: - if ".fix_" in fix: - unwanted_fixes.add(fix) - else: - # Infer the full module name for the fixer. - # First ensure that no names clash (e.g. - # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah): - found = [f for f in avail_fixes - if f.endswith('fix_{0}'.format(fix))] - if len(found) > 1: - print("Ambiguous fixer name. Choose a fully qualified " - "module name instead from these:\n" + - "\n".join(" " + myf for myf in found), - file=sys.stderr) - return 2 - elif len(found) == 0: - print("Unknown fixer. Use --list-fixes or -l for a list.", - file=sys.stderr) - return 2 - unwanted_fixes.add(found[0]) - - extra_fixes = set() - if options.all_imports: - if options.stage1: - prefix = 'libfuturize.fixes.' - extra_fixes.add(prefix + - 'fix_add__future__imports_except_unicode_literals') - else: - # In case the user hasn't run stage1 for some reason: - prefix = 'libpasteurize.fixes.' - extra_fixes.add(prefix + 'fix_add_all__future__imports') - extra_fixes.add(prefix + 'fix_add_future_standard_library_import') - extra_fixes.add(prefix + 'fix_add_all_future_builtins') - explicit = set() - if options.fix: - all_present = False - for fix in options.fix: - if fix == 'all': - all_present = True - else: - if ".fix_" in fix: - explicit.add(fix) - else: - # Infer the full module name for the fixer. - # First ensure that no names clash (e.g. - # lib2to3.fixes.fix_blah and libfuturize.fixes.fix_blah): - found = [f for f in avail_fixes - if f.endswith('fix_{0}'.format(fix))] - if len(found) > 1: - print("Ambiguous fixer name. Choose a fully qualified " - "module name instead from these:\n" + - "\n".join(" " + myf for myf in found), - file=sys.stderr) - return 2 - elif len(found) == 0: - print("Unknown fixer. Use --list-fixes or -l for a list.", - file=sys.stderr) - return 2 - explicit.add(found[0]) - if len(explicit & unwanted_fixes) > 0: - print("Conflicting usage: the following fixers have been " - "simultaneously requested and disallowed:\n" + - "\n".join(" " + myf for myf in (explicit & unwanted_fixes)), - file=sys.stderr) - return 2 - requested = avail_fixes.union(explicit) if all_present else explicit - else: - requested = avail_fixes.union(explicit) - fixer_names = (requested | extra_fixes) - unwanted_fixes - - input_base_dir = os.path.commonprefix(args) - if (input_base_dir and not input_base_dir.endswith(os.sep) - and not os.path.isdir(input_base_dir)): - # One or more similar names were passed, their directory is the base. - # os.path.commonprefix() is ignorant of path elements, this corrects - # for that weird API. - input_base_dir = os.path.dirname(input_base_dir) - if options.output_dir: - input_base_dir = input_base_dir.rstrip(os.sep) - logger.info('Output in %r will mirror the input directory %r layout.', - options.output_dir, input_base_dir) - - # Initialize the refactoring tool - if future.utils.PY26: - extra_kwargs = {} - else: - extra_kwargs = { - 'append_suffix': options.add_suffix, - 'output_dir': options.output_dir, - 'input_base_dir': input_base_dir, - } - - rt = StdoutRefactoringTool( - sorted(fixer_names), flags, sorted(explicit), - options.nobackups, not options.no_diffs, - **extra_kwargs) - - # Refactor all files and directories passed as arguments - if not rt.errors: - if refactor_stdin: - rt.refactor_stdin() - else: - try: - rt.refactor(args, options.write, None, - options.processes) - except refactor.MultiprocessingUnsupported: - assert options.processes > 1 - print("Sorry, -j isn't " \ - "supported on this platform.", file=sys.stderr) - return 1 - rt.summarize() - - # Return error status (0 if rt.errors is zero) - return int(bool(rt.errors)) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/feature_base.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -u""" -Base classes for features that are backwards-incompatible. - -Usage: -features = Features() -features.add(Feature("py3k_feature", "power< 'py3k' any* >", "2.7")) -PATTERN = features.PATTERN -""" - -pattern_unformatted = u"%s=%s" # name=pattern, for dict lookups -message_unformatted = u""" -%s is only supported in Python %s and above.""" - -class Feature(object): - u""" - A feature has a name, a pattern, and a minimum version of Python 2.x - required to use the feature (or 3.x if there is no backwards-compatible - version of 2.x) - """ - def __init__(self, name, PATTERN, version): - self.name = name - self._pattern = PATTERN - self.version = version - - def message_text(self): - u""" - Format the above text with the name and minimum version required. - """ - return message_unformatted % (self.name, self.version) - -class Features(set): - u""" - A set of features that generates a pattern for the features it contains. - This set will act like a mapping in that we map names to patterns. - """ - mapping = {} - - def update_mapping(self): - u""" - Called every time we care about the mapping of names to features. - """ - self.mapping = dict([(f.name, f) for f in iter(self)]) - - @property - def PATTERN(self): - u""" - Uses the mapping of names to features to return a PATTERN suitable - for using the lib2to3 patcomp. - """ - self.update_mapping() - return u" |\n".join([pattern_unformatted % (f.name, f._pattern) for f in iter(self)]) - - def __getitem__(self, key): - u""" - Implement a simple mapping to get patterns from names. - """ - return self.mapping[key] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all_future_builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line:: - - from builtins import (ascii, bytes, chr, dict, filter, hex, input, - int, list, map, next, object, oct, open, pow, - range, round, str, super, zip) - -to a module, irrespective of whether each definition is used. - -Adds these imports after any other imports (in an initial block of them). -""" - -from __future__ import unicode_literals - -from lib2to3 import fixer_base - -from libfuturize.fixer_util import touch_import_top - - -class FixAddAllFutureBuiltins(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 1 - - def transform(self, node, results): - # import_str = """(ascii, bytes, chr, dict, filter, hex, input, - # int, list, map, next, object, oct, open, pow, - # range, round, str, super, zip)""" - touch_import_top(u'builtins', '*', node) - - # builtins = """ascii bytes chr dict filter hex input - # int list map next object oct open pow - # range round str super zip""" - # for builtin in sorted(builtins.split(), reverse=True): - # touch_import_top(u'builtins', builtin, node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_all__future__imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -""" -Fixer for adding: - - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - from __future__ import unicode_literals - -This is done when converting from Py3 to both Py3/Py2. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixAddAllFutureImports(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 1 - - def transform(self, node, results): - future_import(u"unicode_literals", node) - future_import(u"print_function", node) - future_import(u"division", node) - future_import(u"absolute_import", node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_add_future_standard_library_import.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -""" -For the ``future`` package. - -Adds this import line: - - from future import standard_library - -after any __future__ imports but before any other imports. Doesn't actually -change the imports to Py3 style. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import touch_import_top - -class FixAddFutureStandardLibraryImport(fixer_base.BaseFix): - BM_compatible = True - PATTERN = "file_input" - run_order = 8 - - def transform(self, node, results): - # TODO: add a blank line between any __future__ imports and this? - touch_import_top(u'future', u'standard_library', node) - # TODO: also add standard_library.install_hooks() diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_annotations.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -u""" -Fixer to remove function annotations -""" - -from lib2to3 import fixer_base -from lib2to3.pgen2 import token -from lib2to3.fixer_util import syms - -warning_text = u"Removing function annotations completely." - -def param_without_annotations(node): - return node.children[0] - -class FixAnnotations(fixer_base.BaseFix): - - warned = False - - def warn_once(self, node, reason): - if not self.warned: - self.warned = True - self.warning(node, reason=reason) - - PATTERN = u""" - funcdef< 'def' any parameters< '(' [params=any] ')' > ['->' ret=any] ':' any* > - """ - - def transform(self, node, results): - u""" - This just strips annotations from the funcdef completely. - """ - params = results.get(u"params") - ret = results.get(u"ret") - if ret is not None: - assert ret.prev_sibling.type == token.RARROW, u"Invalid return annotation" - self.warn_once(node, reason=warning_text) - ret.prev_sibling.remove() - ret.remove() - if params is None: return - if params.type == syms.typedargslist: - # more than one param in a typedargslist - for param in params.children: - if param.type == syms.tname: - self.warn_once(node, reason=warning_text) - param.replace(param_without_annotations(param)) - elif params.type == syms.tname: - # one param - self.warn_once(node, reason=warning_text) - params.replace(param_without_annotations(params)) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_division.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -u""" -Fixer for division: from __future__ import division if needed -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import token, future_import - -def match_division(node): - u""" - __future__.division redefines the meaning of a single slash for division, - so we match that and only that. - """ - slash = token.SLASH - return node.type == slash and not node.next_sibling.type == slash and \ - not node.prev_sibling.type == slash - -class FixDivision(fixer_base.BaseFix): - run_order = 4 # this seems to be ignored? - - def match(self, node): - u""" - Since the tree needs to be fixed once and only once if and only if it - matches, then we can start discarding matches after we make the first. - """ - return match_division(node) - - def transform(self, node, results): - future_import(u"division", node) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_features.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -u""" -Warn about features that are not present in Python 2.5, giving a message that -points to the earliest version of Python 2.x (or 3.x, if none) that supports it -""" - -from .feature_base import Feature, Features -from lib2to3 import fixer_base - -FEATURES = [ - #(FeatureName, - # FeaturePattern, - # FeatureMinVersion, - #), - (u"memoryview", - u"power < 'memoryview' trailer < '(' any* ')' > any* >", - u"2.7", - ), - (u"numbers", - u"""import_from< 'from' 'numbers' 'import' any* > | - import_name< 'import' ('numbers' dotted_as_names< any* 'numbers' any* >) >""", - u"2.6", - ), - (u"abc", - u"""import_name< 'import' ('abc' dotted_as_names< any* 'abc' any* >) > | - import_from< 'from' 'abc' 'import' any* >""", - u"2.6", - ), - (u"io", - u"""import_name< 'import' ('io' dotted_as_names< any* 'io' any* >) > | - import_from< 'from' 'io' 'import' any* >""", - u"2.6", - ), - (u"bin", - u"power< 'bin' trailer< '(' any* ')' > any* >", - u"2.6", - ), - (u"formatting", - u"power< any trailer< '.' 'format' > trailer< '(' any* ')' > >", - u"2.6", - ), - (u"nonlocal", - u"global_stmt< 'nonlocal' any* >", - u"3.0", - ), - (u"with_traceback", - u"trailer< '.' 'with_traceback' >", - u"3.0", - ), -] - -class FixFeatures(fixer_base.BaseFix): - - run_order = 9 # Wait until all other fixers have run to check for these - - # To avoid spamming, we only want to warn for each feature once. - features_warned = set() - - # Build features from the list above - features = Features([Feature(name, pattern, version) for \ - name, pattern, version in FEATURES]) - - PATTERN = features.PATTERN - - def match(self, node): - to_ret = super(FixFeatures, self).match(node) - # We want the mapping only to tell us the node's specific information. - try: - del to_ret[u'node'] - except Exception: - # We want it to delete the 'node' from the results - # if it's there, so we don't care if it fails for normal reasons. - pass - return to_ret - - def transform(self, node, results): - for feature_name in results: - if feature_name in self.features_warned: - continue - else: - curr_feature = self.features[feature_name] - if curr_feature.version >= u"3": - fail = self.cannot_convert - else: - fail = self.warning - fail(node, reason=curr_feature.message_text()) - self.features_warned.add(feature_name) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_fullargspec.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -u""" -Fixer for getfullargspec -> getargspec -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name - -warn_msg = u"some of the values returned by getfullargspec are not valid in Python 2 and have no equivalent." - -class FixFullargspec(fixer_base.BaseFix): - - PATTERN = u"'getfullargspec'" - - def transform(self, node, results): - self.warning(node, warn_msg) - return Name(u"getargspec", prefix=node.prefix) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_future_builtins.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -""" -Adds this import line: - - from builtins import XYZ - -for each of the functions XYZ that is used in the module. -""" - -from __future__ import unicode_literals - -from lib2to3 import fixer_base -from lib2to3.pygram import python_symbols as syms -from lib2to3.fixer_util import Name, Call, in_special_context - -from libfuturize.fixer_util import touch_import_top - -# All builtins are: -# from future.builtins.iterators import (filter, map, zip) -# from future.builtins.misc import (ascii, chr, hex, input, isinstance, oct, open, round, super) -# from future.types import (bytes, dict, int, range, str) -# We don't need isinstance any more. - -replaced_builtins = '''filter map zip - ascii chr hex input next oct open round super - bytes dict int range str'''.split() - -expression = '|'.join(["name='{0}'".format(name) for name in replaced_builtins]) - - -class FixFutureBuiltins(fixer_base.BaseFix): - BM_compatible = True - run_order = 9 - - # Currently we only match uses as a function. This doesn't match e.g.: - # if isinstance(s, str): - # ... - PATTERN = """ - power< - ({0}) trailer< '(' args=[any] ')' > - rest=any* > - """.format(expression) - - def transform(self, node, results): - name = results["name"] - touch_import_top(u'builtins', name.value, node) - # name.replace(Name(u"input", prefix=name.prefix)) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_getcwd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -u""" -Fixer for os.getcwd() -> os.getcwdu(). -Also warns about "from os import getcwd", suggesting the above form. -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name - -class FixGetcwd(fixer_base.BaseFix): - - PATTERN = u""" - power< 'os' trailer< dot='.' name='getcwd' > any* > - | - import_from< 'from' 'os' 'import' bad='getcwd' > - """ - - def transform(self, node, results): - if u"name" in results: - name = results[u"name"] - name.replace(Name(u"getcwdu", prefix=name.prefix)) - elif u"bad" in results: - # Can't convert to getcwdu and then expect to catch every use. - self.cannot_convert(node, u"import os, use os.getcwd() instead.") - return - else: - raise ValueError(u"For some reason, the pattern matcher failed.") diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports2.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -u""" -Fixer for complicated imports -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, String, FromImport, Newline, Comma -from libfuturize.fixer_util import touch_import_top - - -TK_BASE_NAMES = (u'ACTIVE', u'ALL', u'ANCHOR', u'ARC',u'BASELINE', u'BEVEL', u'BOTH', - u'BOTTOM', u'BROWSE', u'BUTT', u'CASCADE', u'CENTER', u'CHAR', - u'CHECKBUTTON', u'CHORD', u'COMMAND', u'CURRENT', u'DISABLED', - u'DOTBOX', u'E', u'END', u'EW', u'EXCEPTION', u'EXTENDED', u'FALSE', - u'FIRST', u'FLAT', u'GROOVE', u'HIDDEN', u'HORIZONTAL', u'INSERT', - u'INSIDE', u'LAST', u'LEFT', u'MITER', u'MOVETO', u'MULTIPLE', u'N', - u'NE', u'NO', u'NONE', u'NORMAL', u'NS', u'NSEW', u'NUMERIC', u'NW', - u'OFF', u'ON', u'OUTSIDE', u'PAGES', u'PIESLICE', u'PROJECTING', - u'RADIOBUTTON', u'RAISED', u'READABLE', u'RIDGE', u'RIGHT', - u'ROUND', u'S', u'SCROLL', u'SE', u'SEL', u'SEL_FIRST', u'SEL_LAST', - u'SEPARATOR', u'SINGLE', u'SOLID', u'SUNKEN', u'SW', u'StringTypes', - u'TOP', u'TRUE', u'TclVersion', u'TkVersion', u'UNDERLINE', - u'UNITS', u'VERTICAL', u'W', u'WORD', u'WRITABLE', u'X', u'Y', u'YES', - u'wantobjects') - -PY2MODULES = { - u'urllib2' : ( - u'AbstractBasicAuthHandler', u'AbstractDigestAuthHandler', - u'AbstractHTTPHandler', u'BaseHandler', u'CacheFTPHandler', - u'FTPHandler', u'FileHandler', u'HTTPBasicAuthHandler', - u'HTTPCookieProcessor', u'HTTPDefaultErrorHandler', - u'HTTPDigestAuthHandler', u'HTTPError', u'HTTPErrorProcessor', - u'HTTPHandler', u'HTTPPasswordMgr', - u'HTTPPasswordMgrWithDefaultRealm', u'HTTPRedirectHandler', - u'HTTPSHandler', u'OpenerDirector', u'ProxyBasicAuthHandler', - u'ProxyDigestAuthHandler', u'ProxyHandler', u'Request', - u'StringIO', u'URLError', u'UnknownHandler', u'addinfourl', - u'build_opener', u'install_opener', u'parse_http_list', - u'parse_keqv_list', u'randombytes', u'request_host', u'urlopen'), - u'urllib' : ( - u'ContentTooShortError', u'FancyURLopener',u'URLopener', - u'basejoin', u'ftperrors', u'getproxies', - u'getproxies_environment', u'localhost', u'pathname2url', - u'quote', u'quote_plus', u'splitattr', u'splithost', - u'splitnport', u'splitpasswd', u'splitport', u'splitquery', - u'splittag', u'splittype', u'splituser', u'splitvalue', - u'thishost', u'unquote', u'unquote_plus', u'unwrap', - u'url2pathname', u'urlcleanup', u'urlencode', u'urlopen', - u'urlretrieve',), - u'urlparse' : ( - u'parse_qs', u'parse_qsl', u'urldefrag', u'urljoin', - u'urlparse', u'urlsplit', u'urlunparse', u'urlunsplit'), - u'dbm' : ( - u'ndbm', u'gnu', u'dumb'), - u'anydbm' : ( - u'error', u'open'), - u'whichdb' : ( - u'whichdb',), - u'BaseHTTPServer' : ( - u'BaseHTTPRequestHandler', u'HTTPServer'), - u'CGIHTTPServer' : ( - u'CGIHTTPRequestHandler',), - u'SimpleHTTPServer' : ( - u'SimpleHTTPRequestHandler',), - u'FileDialog' : TK_BASE_NAMES + ( - u'FileDialog', u'LoadFileDialog', u'SaveFileDialog', - u'dialogstates', u'test'), - u'tkFileDialog' : ( - u'Directory', u'Open', u'SaveAs', u'_Dialog', u'askdirectory', - u'askopenfile', u'askopenfilename', u'askopenfilenames', - u'askopenfiles', u'asksaveasfile', u'asksaveasfilename'), - u'SimpleDialog' : TK_BASE_NAMES + ( - u'SimpleDialog',), - u'tkSimpleDialog' : TK_BASE_NAMES + ( - u'askfloat', u'askinteger', u'askstring', u'Dialog'), - u'SimpleXMLRPCServer' : ( - u'CGIXMLRPCRequestHandler', u'SimpleXMLRPCDispatcher', - u'SimpleXMLRPCRequestHandler', u'SimpleXMLRPCServer', - u'list_public_methods', u'remove_duplicates', - u'resolve_dotted_attribute'), - u'DocXMLRPCServer' : ( - u'DocCGIXMLRPCRequestHandler', u'DocXMLRPCRequestHandler', - u'DocXMLRPCServer', u'ServerHTMLDoc',u'XMLRPCDocGenerator'), - } - -MAPPING = { u'urllib.request' : - (u'urllib2', u'urllib'), - u'urllib.error' : - (u'urllib2', u'urllib'), - u'urllib.parse' : - (u'urllib2', u'urllib', u'urlparse'), - u'dbm.__init__' : - (u'anydbm', u'whichdb'), - u'http.server' : - (u'CGIHTTPServer', u'SimpleHTTPServer', u'BaseHTTPServer'), - u'tkinter.filedialog' : - (u'tkFileDialog', u'FileDialog'), - u'tkinter.simpledialog' : - (u'tkSimpleDialog', u'SimpleDialog'), - u'xmlrpc.server' : - (u'DocXMLRPCServer', u'SimpleXMLRPCServer'), - } - -# helps match 'http', as in 'from http.server import ...' -simple_name = u"name='%s'" -# helps match 'server', as in 'from http.server import ...' -simple_attr = u"attr='%s'" -# helps match 'HTTPServer', as in 'from http.server import HTTPServer' -simple_using = u"using='%s'" -# helps match 'urllib.request', as in 'import urllib.request' -dotted_name = u"dotted_name=dotted_name< %s '.' %s >" -# helps match 'http.server', as in 'http.server.HTTPServer(...)' -power_twoname = u"pow=power< %s trailer< '.' %s > trailer< '.' using=any > any* >" -# helps match 'dbm.whichdb', as in 'dbm.whichdb(...)' -power_onename = u"pow=power< %s trailer< '.' using=any > any* >" -# helps match 'from http.server import HTTPServer' -# also helps match 'from http.server import HTTPServer, SimpleHTTPRequestHandler' -# also helps match 'from http.server import *' -from_import = u"from_import=import_from< 'from' %s 'import' (import_as_name< using=any 'as' renamed=any> | in_list=import_as_names< using=any* > | using='*' | using=NAME) >" -# helps match 'import urllib.request' -name_import = u"name_import=import_name< 'import' (%s | in_list=dotted_as_names< imp_list=any* >) >" - -############# -# WON'T FIX # -############# - -# helps match 'import urllib.request as name' -name_import_rename = u"name_import_rename=dotted_as_name< %s 'as' renamed=any >" -# helps match 'from http import server' -from_import_rename = u"from_import_rename=import_from< 'from' %s 'import' (%s | import_as_name< %s 'as' renamed=any > | in_list=import_as_names< any* (%s | import_as_name< %s 'as' renamed=any >) any* >) >" - - -def all_modules_subpattern(): - u""" - Builds a pattern for all toplevel names - (urllib, http, etc) - """ - names_dot_attrs = [mod.split(u".") for mod in MAPPING] - ret = u"( " + u" | ".join([dotted_name % (simple_name % (mod[0]), - simple_attr % (mod[1])) for mod in names_dot_attrs]) - ret += u" | " - ret += u" | ".join([simple_name % (mod[0]) for mod in names_dot_attrs if mod[1] == u"__init__"]) + u" )" - return ret - - -def build_import_pattern(mapping1, mapping2): - u""" - mapping1: A dict mapping py3k modules to all possible py2k replacements - mapping2: A dict mapping py2k modules to the things they do - This builds a HUGE pattern to match all ways that things can be imported - """ - # py3k: urllib.request, py2k: ('urllib2', 'urllib') - yield from_import % (all_modules_subpattern()) - for py3k, py2k in mapping1.items(): - name, attr = py3k.split(u'.') - s_name = simple_name % (name) - s_attr = simple_attr % (attr) - d_name = dotted_name % (s_name, s_attr) - yield name_import % (d_name) - yield power_twoname % (s_name, s_attr) - if attr == u'__init__': - yield name_import % (s_name) - yield power_onename % (s_name) - yield name_import_rename % (d_name) - yield from_import_rename % (s_name, s_attr, s_attr, s_attr, s_attr) - - -class FixImports2(fixer_base.BaseFix): - - run_order = 4 - - PATTERN = u" | \n".join(build_import_pattern(MAPPING, PY2MODULES)) - - def transform(self, node, results): - touch_import_top(u'future', u'standard_library', node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_imports.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -u""" -Fixer for standard library imports renamed in Python 3 -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, is_probably_builtin, Newline, does_tree_import -from lib2to3.pygram import python_symbols as syms -from lib2to3.pgen2 import token -from lib2to3.pytree import Node, Leaf - -from libfuturize.fixer_util import touch_import_top -# from ..fixer_util import NameImport - -# used in simple_mapping_to_pattern() -MAPPING = {u"reprlib": u"repr", - u"winreg": u"_winreg", - u"configparser": u"ConfigParser", - u"copyreg": u"copy_reg", - u"queue": u"Queue", - u"socketserver": u"SocketServer", - u"_markupbase": u"markupbase", - u"test.support": u"test.test_support", - u"dbm.bsd": u"dbhash", - u"dbm.ndbm": u"dbm", - u"dbm.dumb": u"dumbdbm", - u"dbm.gnu": u"gdbm", - u"html.parser": u"HTMLParser", - u"html.entities": u"htmlentitydefs", - u"http.client": u"httplib", - u"http.cookies": u"Cookie", - u"http.cookiejar": u"cookielib", -# "tkinter": "Tkinter", - u"tkinter.dialog": u"Dialog", - u"tkinter._fix": u"FixTk", - u"tkinter.scrolledtext": u"ScrolledText", - u"tkinter.tix": u"Tix", - u"tkinter.constants": u"Tkconstants", - u"tkinter.dnd": u"Tkdnd", - u"tkinter.__init__": u"Tkinter", - u"tkinter.colorchooser": u"tkColorChooser", - u"tkinter.commondialog": u"tkCommonDialog", - u"tkinter.font": u"tkFont", - u"tkinter.ttk": u"ttk", - u"tkinter.messagebox": u"tkMessageBox", - u"tkinter.turtle": u"turtle", - u"urllib.robotparser": u"robotparser", - u"xmlrpc.client": u"xmlrpclib", - u"builtins": u"__builtin__", -} - -# generic strings to help build patterns -# these variables mean (with http.client.HTTPConnection as an example): -# name = http -# attr = client -# used = HTTPConnection -# fmt_name is a formatted subpattern (simple_name_match or dotted_name_match) - -# helps match 'queue', as in 'from queue import ...' -simple_name_match = u"name='%s'" -# helps match 'client', to be used if client has been imported from http -subname_match = u"attr='%s'" -# helps match 'http.client', as in 'import urllib.request' -dotted_name_match = u"dotted_name=dotted_name< %s '.' %s >" -# helps match 'queue', as in 'queue.Queue(...)' -power_onename_match = u"%s" -# helps match 'http.client', as in 'http.client.HTTPConnection(...)' -power_twoname_match = u"power< %s trailer< '.' %s > any* >" -# helps match 'client.HTTPConnection', if 'client' has been imported from http -power_subname_match = u"power< %s any* >" -# helps match 'from http.client import HTTPConnection' -from_import_match = u"from_import=import_from< 'from' %s 'import' imported=any >" -# helps match 'from http import client' -from_import_submod_match = u"from_import_submod=import_from< 'from' %s 'import' (%s | import_as_name< %s 'as' renamed=any > | import_as_names< any* (%s | import_as_name< %s 'as' renamed=any >) any* > ) >" -# helps match 'import urllib.request' -name_import_match = u"name_import=import_name< 'import' %s > | name_import=import_name< 'import' dotted_as_name< %s 'as' renamed=any > >" -# helps match 'import http.client, winreg' -multiple_name_import_match = u"name_import=import_name< 'import' dotted_as_names< names=any* > >" - -def all_patterns(name): - u""" - Accepts a string and returns a pattern of possible patterns involving that name - Called by simple_mapping_to_pattern for each name in the mapping it receives. - """ - - # i_ denotes an import-like node - # u_ denotes a node that appears to be a usage of the name - if u'.' in name: - name, attr = name.split(u'.', 1) - simple_name = simple_name_match % (name) - simple_attr = subname_match % (attr) - dotted_name = dotted_name_match % (simple_name, simple_attr) - i_from = from_import_match % (dotted_name) - i_from_submod = from_import_submod_match % (simple_name, simple_attr, simple_attr, simple_attr, simple_attr) - i_name = name_import_match % (dotted_name, dotted_name) - u_name = power_twoname_match % (simple_name, simple_attr) - u_subname = power_subname_match % (simple_attr) - return u' | \n'.join((i_name, i_from, i_from_submod, u_name, u_subname)) - else: - simple_name = simple_name_match % (name) - i_name = name_import_match % (simple_name, simple_name) - i_from = from_import_match % (simple_name) - u_name = power_onename_match % (simple_name) - return u' | \n'.join((i_name, i_from, u_name)) - - -class FixImports(fixer_base.BaseFix): - - PATTERN = u' | \n'.join([all_patterns(name) for name in MAPPING]) - PATTERN = u' | \n'.join((PATTERN, multiple_name_import_match)) - - def transform(self, node, results): - touch_import_top(u'future', u'standard_library', node) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_kwargs.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -u""" -Fixer for Python 3 function parameter syntax -This fixer is rather sensitive to incorrect py3k syntax. -""" - -# Note: "relevant" parameters are parameters following the first STAR in the list. - -from lib2to3 import fixer_base -from lib2to3.fixer_util import token, String, Newline, Comma, Name -from libfuturize.fixer_util import indentation, suitify, DoubleStar - -_assign_template = u"%(name)s = %(kwargs)s['%(name)s']; del %(kwargs)s['%(name)s']" -_if_template = u"if '%(name)s' in %(kwargs)s: %(assign)s" -_else_template = u"else: %(name)s = %(default)s" -_kwargs_default_name = u"_3to2kwargs" - -def gen_params(raw_params): - u""" - Generator that yields tuples of (name, default_value) for each parameter in the list - If no default is given, then it is default_value is None (not Leaf(token.NAME, 'None')) - """ - assert raw_params[0].type == token.STAR and len(raw_params) > 2 - curr_idx = 2 # the first place a keyword-only parameter name can be is index 2 - max_idx = len(raw_params) - while curr_idx < max_idx: - curr_item = raw_params[curr_idx] - prev_item = curr_item.prev_sibling - if curr_item.type != token.NAME: - curr_idx += 1 - continue - if prev_item is not None and prev_item.type == token.DOUBLESTAR: - break - name = curr_item.value - nxt = curr_item.next_sibling - if nxt is not None and nxt.type == token.EQUAL: - default_value = nxt.next_sibling - curr_idx += 2 - else: - default_value = None - yield (name, default_value) - curr_idx += 1 - -def remove_params(raw_params, kwargs_default=_kwargs_default_name): - u""" - Removes all keyword-only args from the params list and a bare star, if any. - Does not add the kwargs dict if needed. - Returns True if more action is needed, False if not - (more action is needed if no kwargs dict exists) - """ - assert raw_params[0].type == token.STAR - if raw_params[1].type == token.COMMA: - raw_params[0].remove() - raw_params[1].remove() - kw_params = raw_params[2:] - else: - kw_params = raw_params[3:] - for param in kw_params: - if param.type != token.DOUBLESTAR: - param.remove() - else: - return False - else: - return True - -def needs_fixing(raw_params, kwargs_default=_kwargs_default_name): - u""" - Returns string with the name of the kwargs dict if the params after the first star need fixing - Otherwise returns empty string - """ - found_kwargs = False - needs_fix = False - - for t in raw_params[2:]: - if t.type == token.COMMA: - # Commas are irrelevant at this stage. - continue - elif t.type == token.NAME and not found_kwargs: - # Keyword-only argument: definitely need to fix. - needs_fix = True - elif t.type == token.NAME and found_kwargs: - # Return 'foobar' of **foobar, if needed. - return t.value if needs_fix else u'' - elif t.type == token.DOUBLESTAR: - # Found either '*' from **foobar. - found_kwargs = True - else: - # Never found **foobar. Return a synthetic name, if needed. - return kwargs_default if needs_fix else u'' - -class FixKwargs(fixer_base.BaseFix): - - run_order = 7 # Run after function annotations are removed - - PATTERN = u"funcdef< 'def' NAME parameters< '(' arglist=typedargslist< params=any* > ')' > ':' suite=any >" - - def transform(self, node, results): - params_rawlist = results[u"params"] - for i, item in enumerate(params_rawlist): - if item.type == token.STAR: - params_rawlist = params_rawlist[i:] - break - else: - return - # params is guaranteed to be a list starting with *. - # if fixing is needed, there will be at least 3 items in this list: - # [STAR, COMMA, NAME] is the minimum that we need to worry about. - new_kwargs = needs_fixing(params_rawlist) - # new_kwargs is the name of the kwargs dictionary. - if not new_kwargs: - return - suitify(node) - - # At this point, params_rawlist is guaranteed to be a list - # beginning with a star that includes at least one keyword-only param - # e.g., [STAR, NAME, COMMA, NAME, COMMA, DOUBLESTAR, NAME] or - # [STAR, COMMA, NAME], or [STAR, COMMA, NAME, COMMA, DOUBLESTAR, NAME] - - # Anatomy of a funcdef: ['def', 'name', parameters, ':', suite] - # Anatomy of that suite: [NEWLINE, INDENT, first_stmt, all_other_stmts] - # We need to insert our new stuff before the first_stmt and change the - # first_stmt's prefix. - - suite = node.children[4] - first_stmt = suite.children[2] - ident = indentation(first_stmt) - - for name, default_value in gen_params(params_rawlist): - if default_value is None: - suite.insert_child(2, Newline()) - suite.insert_child(2, String(_assign_template %{u'name':name, u'kwargs':new_kwargs}, prefix=ident)) - else: - suite.insert_child(2, Newline()) - suite.insert_child(2, String(_else_template %{u'name':name, u'default':default_value}, prefix=ident)) - suite.insert_child(2, Newline()) - suite.insert_child(2, String(_if_template %{u'assign':_assign_template %{u'name':name, u'kwargs':new_kwargs}, u'name':name, u'kwargs':new_kwargs}, prefix=ident)) - first_stmt.prefix = ident - suite.children[2].prefix = u"" - - # Now, we need to fix up the list of params. - - must_add_kwargs = remove_params(params_rawlist) - if must_add_kwargs: - arglist = results[u'arglist'] - if len(arglist.children) > 0 and arglist.children[-1].type != token.COMMA: - arglist.append_child(Comma()) - arglist.append_child(DoubleStar(prefix=u" ")) - arglist.append_child(Name(new_kwargs)) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_memoryview.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -u""" -Fixer for memoryview(s) -> buffer(s). -Explicit because some memoryview methods are invalid on buffer objects. -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name - - -class FixMemoryview(fixer_base.BaseFix): - - explicit = True # User must specify that they want this. - - PATTERN = u""" - power< name='memoryview' trailer< '(' [any] ')' > - rest=any* > - """ - - def transform(self, node, results): - name = results[u"name"] - name.replace(Name(u"buffer", prefix=name.prefix)) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_metaclass.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -u""" -Fixer for (metaclass=X) -> __metaclass__ = X -Some semantics (see PEP 3115) may be altered in the translation.""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, syms, Node, Leaf, Newline, find_root -from lib2to3.pygram import token -from libfuturize.fixer_util import indentation, suitify -# from ..fixer_util import Name, syms, Node, Leaf, Newline, find_root, indentation, suitify - -def has_metaclass(parent): - results = None - for node in parent.children: - kids = node.children - if node.type == syms.argument: - if kids[0] == Leaf(token.NAME, u"metaclass") and \ - kids[1] == Leaf(token.EQUAL, u"=") and \ - kids[2]: - #Hack to avoid "class X(=):" with this case. - results = [node] + kids - break - elif node.type == syms.arglist: - # Argument list... loop through it looking for: - # Node(*, [*, Leaf(token.NAME, u"metaclass"), Leaf(token.EQUAL, u"="), Leaf(*, *)] - for child in node.children: - if results: break - if child.type == token.COMMA: - #Store the last comma, which precedes the metaclass - comma = child - elif type(child) == Node: - meta = equal = name = None - for arg in child.children: - if arg == Leaf(token.NAME, u"metaclass"): - #We have the (metaclass) part - meta = arg - elif meta and arg == Leaf(token.EQUAL, u"="): - #We have the (metaclass=) part - equal = arg - elif meta and equal: - #Here we go, we have (metaclass=X) - name = arg - results = (comma, meta, equal, name) - break - return results - - -class FixMetaclass(fixer_base.BaseFix): - - PATTERN = u""" - classdef - """ - - def transform(self, node, results): - meta_results = has_metaclass(node) - if not meta_results: return - for meta in meta_results: - meta.remove() - target = Leaf(token.NAME, u"__metaclass__") - equal = Leaf(token.EQUAL, u"=", prefix=u" ") - # meta is the last item in what was returned by has_metaclass(): name - name = meta - name.prefix = u" " - stmt_node = Node(syms.atom, [target, equal, name]) - - suitify(node) - for item in node.children: - if item.type == syms.suite: - for stmt in item.children: - if stmt.type == token.INDENT: - # Insert, in reverse order, the statement, a newline, - # and an indent right after the first indented line - loc = item.children.index(stmt) + 1 - # Keep consistent indentation form - ident = Leaf(token.INDENT, stmt.value) - item.insert_child(loc, ident) - item.insert_child(loc, Newline()) - item.insert_child(loc, stmt_node) - break diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_newstyle.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -u""" -Fixer for "class Foo: ..." -> "class Foo(object): ..." -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import LParen, RParen, Name - -from libfuturize.fixer_util import touch_import_top - - -def insert_object(node, idx): - node.insert_child(idx, RParen()) - node.insert_child(idx, Name(u"object")) - node.insert_child(idx, LParen()) - -class FixNewstyle(fixer_base.BaseFix): - - # Match: - # class Blah: - # and: - # class Blah(): - - PATTERN = u"classdef< 'class' NAME ['(' ')'] colon=':' any >" - - def transform(self, node, results): - colon = results[u"colon"] - idx = node.children.index(colon) - if (node.children[idx-2].value == '(' and - node.children[idx-1].value == ')'): - del node.children[idx-2:idx] - idx -= 2 - insert_object(node, idx) - touch_import_top(u'builtins', 'object', node) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_next.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -u""" -Fixer for: -it.__next__() -> it.next(). -next(it) -> it.next(). -""" - -from lib2to3.pgen2 import token -from lib2to3.pygram import python_symbols as syms -from lib2to3 import fixer_base -from lib2to3.fixer_util import Name, Call, find_binding, Attr - -bind_warning = u"Calls to builtin next() possibly shadowed by global binding" - - -class FixNext(fixer_base.BaseFix): - - PATTERN = u""" - power< base=any+ trailer< '.' attr='__next__' > any* > - | - power< head='next' trailer< '(' arg=any ')' > any* > - | - classdef< 'class' base=any+ ':' - suite< any* - funcdef< 'def' - attr='__next__' - parameters< '(' NAME ')' > any+ > - any* > > - """ - - def transform(self, node, results): - assert results - - base = results.get(u"base") - attr = results.get(u"attr") - head = results.get(u"head") - arg_ = results.get(u"arg") - if arg_: - arg = arg_.clone() - head.replace(Attr(Name(unicode(arg),prefix=head.prefix), - Name(u"next"))) - arg_.remove() - elif base: - attr.replace(Name(u"next", prefix=attr.prefix)) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_printfunction.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -u""" -Fixer for print: from __future__ import print_function. -""" - -from lib2to3 import fixer_base -from libfuturize.fixer_util import future_import - -class FixPrintfunction(fixer_base.BaseFix): - - # explicit = True - - PATTERN = u""" - power< 'print' trailer < '(' any* ')' > any* > - """ - - def transform(self, node, results): - future_import(u"print_function", node) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise_.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -u"""Fixer for - raise E(V).with_traceback(T) - to: - from future.utils import raise_ - ... - raise_(E, V, T) - -TODO: FIXME!! - -""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Comma, Node, Leaf, token, syms - -class FixRaise(fixer_base.BaseFix): - - PATTERN = u""" - raise_stmt< 'raise' (power< name=any [trailer< '(' val=any* ')' >] - [trailer< '.' 'with_traceback' > trailer< '(' trc=any ')' >] > | any) ['from' chain=any] >""" - - def transform(self, node, results): - FIXME - name, val, trc = (results.get(u"name"), results.get(u"val"), results.get(u"trc")) - chain = results.get(u"chain") - if chain is not None: - self.warning(node, u"explicit exception chaining is not supported in Python 2") - chain.prev_sibling.remove() - chain.remove() - if trc is not None: - val = val[0] if val else Leaf(token.NAME, u"None") - val.prefix = trc.prefix = u" " - kids = [Leaf(token.NAME, u"raise"), name.clone(), Comma(), - val.clone(), Comma(), trc.clone()] - raise_stmt = Node(syms.raise_stmt, kids) - node.replace(raise_stmt) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_raise.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -u"""Fixer for 'raise E(V).with_traceback(T)' -> 'raise E, V, T'""" - -from lib2to3 import fixer_base -from lib2to3.fixer_util import Comma, Node, Leaf, token, syms - -class FixRaise(fixer_base.BaseFix): - - PATTERN = u""" - raise_stmt< 'raise' (power< name=any [trailer< '(' val=any* ')' >] - [trailer< '.' 'with_traceback' > trailer< '(' trc=any ')' >] > | any) ['from' chain=any] >""" - - def transform(self, node, results): - name, val, trc = (results.get(u"name"), results.get(u"val"), results.get(u"trc")) - chain = results.get(u"chain") - if chain is not None: - self.warning(node, u"explicit exception chaining is not supported in Python 2") - chain.prev_sibling.remove() - chain.remove() - if trc is not None: - val = val[0] if val else Leaf(token.NAME, u"None") - val.prefix = trc.prefix = u" " - kids = [Leaf(token.NAME, u"raise"), name.clone(), Comma(), - val.clone(), Comma(), trc.clone()] - raise_stmt = Node(syms.raise_stmt, kids) - node.replace(raise_stmt) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_throw.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -u"""Fixer for 'g.throw(E(V).with_traceback(T))' -> 'g.throw(E, V, T)'""" - -from lib2to3 import fixer_base -from lib2to3.pytree import Node, Leaf -from lib2to3.pgen2 import token -from lib2to3.fixer_util import Comma - -class FixThrow(fixer_base.BaseFix): - - PATTERN = u""" - power< any trailer< '.' 'throw' > - trailer< '(' args=power< exc=any trailer< '(' val=any* ')' > - trailer< '.' 'with_traceback' > trailer< '(' trc=any ')' > > ')' > > - """ - - def transform(self, node, results): - syms = self.syms - exc, val, trc = (results[u"exc"], results[u"val"], results[u"trc"]) - val = val[0] if val else Leaf(token.NAME, u"None") - val.prefix = trc.prefix = u" " - kids = [exc.clone(), Comma(), val.clone(), Comma(), trc.clone()] - args = results[u"args"] - args.children = kids diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/fix_unpacking.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -u""" -Fixer for: -(a,)* *b (,c)* [,] = s -for (a,)* *b (,c)* [,] in d: ... -""" - -from lib2to3 import fixer_base -from itertools import count -from lib2to3.fixer_util import (Assign, Comma, Call, Newline, Name, - Number, token, syms, Node, Leaf) -from libfuturize.fixer_util import indentation, suitify, commatize -# from libfuturize.fixer_util import Assign, Comma, Call, Newline, Name, Number, indentation, suitify, commatize, token, syms, Node, Leaf - -def assignment_source(num_pre, num_post, LISTNAME, ITERNAME): - u""" - Accepts num_pre and num_post, which are counts of values - before and after the starg (not including the starg) - Returns a source fit for Assign() from fixer_util - """ - children = [] - pre = unicode(num_pre) - post = unicode(num_post) - # This code builds the assignment source from lib2to3 tree primitives. - # It's not very readable, but it seems like the most correct way to do it. - if num_pre > 0: - pre_part = Node(syms.power, [Name(LISTNAME), Node(syms.trailer, [Leaf(token.LSQB, u"["), Node(syms.subscript, [Leaf(token.COLON, u":"), Number(pre)]), Leaf(token.RSQB, u"]")])]) - children.append(pre_part) - children.append(Leaf(token.PLUS, u"+", prefix=u" ")) - main_part = Node(syms.power, [Leaf(token.LSQB, u"[", prefix=u" "), Name(LISTNAME), Node(syms.trailer, [Leaf(token.LSQB, u"["), Node(syms.subscript, [Number(pre) if num_pre > 0 else Leaf(1, u""), Leaf(token.COLON, u":"), Node(syms.factor, [Leaf(token.MINUS, u"-"), Number(post)]) if num_post > 0 else Leaf(1, u"")]), Leaf(token.RSQB, u"]"), Leaf(token.RSQB, u"]")])]) - children.append(main_part) - if num_post > 0: - children.append(Leaf(token.PLUS, u"+", prefix=u" ")) - post_part = Node(syms.power, [Name(LISTNAME, prefix=u" "), Node(syms.trailer, [Leaf(token.LSQB, u"["), Node(syms.subscript, [Node(syms.factor, [Leaf(token.MINUS, u"-"), Number(post)]), Leaf(token.COLON, u":")]), Leaf(token.RSQB, u"]")])]) - children.append(post_part) - source = Node(syms.arith_expr, children) - return source - -class FixUnpacking(fixer_base.BaseFix): - - PATTERN = u""" - expl=expr_stmt< testlist_star_expr< - pre=(any ',')* - star_expr< '*' name=NAME > - post=(',' any)* [','] > '=' source=any > | - impl=for_stmt< 'for' lst=exprlist< - pre=(any ',')* - star_expr< '*' name=NAME > - post=(',' any)* [','] > 'in' it=any ':' suite=any>""" - - def fix_explicit_context(self, node, results): - pre, name, post, source = (results.get(n) for n in (u"pre", u"name", u"post", u"source")) - pre = [n.clone() for n in pre if n.type == token.NAME] - name.prefix = u" " - post = [n.clone() for n in post if n.type == token.NAME] - target = [n.clone() for n in commatize(pre + [name.clone()] + post)] - # to make the special-case fix for "*z, = ..." correct with the least - # amount of modification, make the left-side into a guaranteed tuple - target.append(Comma()) - source.prefix = u"" - setup_line = Assign(Name(self.LISTNAME), Call(Name(u"list"), [source.clone()])) - power_line = Assign(target, assignment_source(len(pre), len(post), self.LISTNAME, self.ITERNAME)) - return setup_line, power_line - - def fix_implicit_context(self, node, results): - u""" - Only example of the implicit context is - a for loop, so only fix that. - """ - pre, name, post, it = (results.get(n) for n in (u"pre", u"name", u"post", u"it")) - pre = [n.clone() for n in pre if n.type == token.NAME] - name.prefix = u" " - post = [n.clone() for n in post if n.type == token.NAME] - target = [n.clone() for n in commatize(pre + [name.clone()] + post)] - # to make the special-case fix for "*z, = ..." correct with the least - # amount of modification, make the left-side into a guaranteed tuple - target.append(Comma()) - source = it.clone() - source.prefix = u"" - setup_line = Assign(Name(self.LISTNAME), Call(Name(u"list"), [Name(self.ITERNAME)])) - power_line = Assign(target, assignment_source(len(pre), len(post), self.LISTNAME, self.ITERNAME)) - return setup_line, power_line - - def transform(self, node, results): - u""" - a,b,c,d,e,f,*g,h,i = range(100) changes to - _3to2list = list(range(100)) - a,b,c,d,e,f,g,h,i, = _3to2list[:6] + [_3to2list[6:-2]] + _3to2list[-2:] - - and - - for a,b,*c,d,e in iter_of_iters: do_stuff changes to - for _3to2iter in iter_of_iters: - _3to2list = list(_3to2iter) - a,b,c,d,e, = _3to2list[:2] + [_3to2list[2:-2]] + _3to2list[-2:] - do_stuff - """ - self.LISTNAME = self.new_name(u"_3to2list") - self.ITERNAME = self.new_name(u"_3to2iter") - expl, impl = results.get(u"expl"), results.get(u"impl") - if expl is not None: - setup_line, power_line = self.fix_explicit_context(node, results) - setup_line.prefix = expl.prefix - power_line.prefix = indentation(expl.parent) - setup_line.append_child(Newline()) - parent = node.parent - i = node.remove() - parent.insert_child(i, power_line) - parent.insert_child(i, setup_line) - elif impl is not None: - setup_line, power_line = self.fix_implicit_context(node, results) - suitify(node) - suite = [k for k in node.children if k.type == syms.suite][0] - setup_line.prefix = u"" - power_line.prefix = suite.children[1].value - suite.children[2].prefix = indentation(suite.children[2]) - suite.insert_child(2, Newline()) - suite.insert_child(2, power_line) - suite.insert_child(2, Newline()) - suite.insert_child(2, setup_line) - results.get(u"lst").replace(Name(self.ITERNAME, prefix=u" ")) diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/fixes/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/fixes/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -import sys -from lib2to3 import refactor - -# The original set of these fixes comes from lib3to2 (https://bitbucket.org/amentajo/lib3to2): -fix_names = set([ - 'libpasteurize.fixes.fix_add_all__future__imports', # from __future__ import absolute_import etc. on separate lines - 'libpasteurize.fixes.fix_add_future_standard_library_import', # we force adding this import for now, even if it doesn't seem necessary to the fix_future_standard_library fixer, for ease of testing - # 'libfuturize.fixes.fix_order___future__imports', # consolidates to a single line to simplify testing -- UNFINISHED - 'libpasteurize.fixes.fix_future_builtins', # adds "from future.builtins import *" - 'libfuturize.fixes.fix_future_standard_library', # adds "from future import standard_library" - - 'libpasteurize.fixes.fix_annotations', - # 'libpasteurize.fixes.fix_bitlength', # ints have this in Py2.7 - # 'libpasteurize.fixes.fix_bool', # need a decorator or Mixin - # 'libpasteurize.fixes.fix_bytes', # leave bytes as bytes - # 'libpasteurize.fixes.fix_classdecorator', # available in - # Py2.6+ - # 'libpasteurize.fixes.fix_collections', hmmm ... - # 'libpasteurize.fixes.fix_dctsetcomp', # avail in Py27 - 'libpasteurize.fixes.fix_division', # yes - # 'libpasteurize.fixes.fix_except', # avail in Py2.6+ - # 'libpasteurize.fixes.fix_features', # ? - 'libpasteurize.fixes.fix_fullargspec', - # 'libpasteurize.fixes.fix_funcattrs', - 'libpasteurize.fixes.fix_getcwd', - 'libpasteurize.fixes.fix_imports', # adds "from future import standard_library" - 'libpasteurize.fixes.fix_imports2', - # 'libpasteurize.fixes.fix_input', - # 'libpasteurize.fixes.fix_int', - # 'libpasteurize.fixes.fix_intern', - # 'libpasteurize.fixes.fix_itertools', - 'libpasteurize.fixes.fix_kwargs', # yes, we want this - # 'libpasteurize.fixes.fix_memoryview', - # 'libpasteurize.fixes.fix_metaclass', # write a custom handler for - # this - # 'libpasteurize.fixes.fix_methodattrs', # __func__ and __self__ seem to be defined on Py2.7 already - 'libpasteurize.fixes.fix_newstyle', # yes, we want this: explicit inheritance from object. Without new-style classes in Py2, super() will break etc. - # 'libpasteurize.fixes.fix_next', # use a decorator for this - # 'libpasteurize.fixes.fix_numliterals', # prob not - # 'libpasteurize.fixes.fix_open', # huh? - # 'libpasteurize.fixes.fix_print', # no way - 'libpasteurize.fixes.fix_printfunction', # adds __future__ import print_function - # 'libpasteurize.fixes.fix_raise_', # TODO: get this working! - - # 'libpasteurize.fixes.fix_range', # nope - # 'libpasteurize.fixes.fix_reduce', - # 'libpasteurize.fixes.fix_setliteral', - # 'libpasteurize.fixes.fix_str', - # 'libpasteurize.fixes.fix_super', # maybe, if our magic super() isn't robust enough - 'libpasteurize.fixes.fix_throw', # yes, if Py3 supports it - # 'libpasteurize.fixes.fix_unittest', - 'libpasteurize.fixes.fix_unpacking', # yes, this is useful - # 'libpasteurize.fixes.fix_with' # way out of date - ]) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -# empty to make this a package diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/main.py pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/main.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/libpasteurize/main.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/libpasteurize/main.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,149 +0,0 @@ -""" -pasteurize: automatic conversion of Python 3 code to clean 2/3 code -=================================================================== - -``pasteurize`` attempts to convert existing Python 3 code into source-compatible -Python 2 and 3 code. - -Use it like this on Python 3 code: - - $ pasteurize --verbose mypython3script.py - -This removes any Py3-only syntax (e.g. new metaclasses) and adds these -import lines: - - from __future__ import absolute_import - from __future__ import division - from __future__ import print_function - from __future__ import unicode_literals - from future import standard_library - standard_library.install_hooks() - from builtins import * - -To write changes to the files, use the -w flag. - -It also adds any other wrappers needed for Py2/3 compatibility. - -Note that separate stages are not available (or needed) when converting from -Python 3 with ``pasteurize`` as they are when converting from Python 2 with -``futurize``. - -The --all-imports option forces adding all ``__future__`` imports, -``builtins`` imports, and standard library aliases, even if they don't -seem necessary for the current state of each module. (This can simplify -testing, and can reduce the need to think about Py2 compatibility when editing -the code further.) - -""" - -from __future__ import (absolute_import, print_function, unicode_literals) - -import sys -import logging -import optparse -from lib2to3.main import main, warn, StdoutRefactoringTool -from lib2to3 import refactor - -from future import __version__ -from libpasteurize.fixes import fix_names - - -def main(args=None): - """Main program. - - Returns a suggested exit status (0, 1, 2). - """ - # Set up option parser - parser = optparse.OptionParser(usage="pasteurize [options] file|dir ...") - parser.add_option("-V", "--version", action="store_true", - help="Report the version number of pasteurize") - parser.add_option("-a", "--all-imports", action="store_true", - help="Adds all __future__ and future imports to each module") - parser.add_option("-f", "--fix", action="append", default=[], - help="Each FIX specifies a transformation; default: all") - parser.add_option("-j", "--processes", action="store", default=1, - type="int", help="Run 2to3 concurrently") - parser.add_option("-x", "--nofix", action="append", default=[], - help="Prevent a fixer from being run.") - parser.add_option("-l", "--list-fixes", action="store_true", - help="List available transformations") - # parser.add_option("-p", "--print-function", action="store_true", - # help="Modify the grammar so that print() is a function") - parser.add_option("-v", "--verbose", action="store_true", - help="More verbose logging") - parser.add_option("--no-diffs", action="store_true", - help="Don't show diffs of the refactoring") - parser.add_option("-w", "--write", action="store_true", - help="Write back modified files") - parser.add_option("-n", "--nobackups", action="store_true", default=False, - help="Don't write backups for modified files.") - - # Parse command line arguments - refactor_stdin = False - flags = {} - options, args = parser.parse_args(args) - fixer_pkg = 'libpasteurize.fixes' - avail_fixes = fix_names - flags["print_function"] = True - - if not options.write and options.no_diffs: - warn("not writing files and not printing diffs; that's not very useful") - if not options.write and options.nobackups: - parser.error("Can't use -n without -w") - if options.version: - print(__version__) - return 0 - if options.list_fixes: - print("Available transformations for the -f/--fix option:") - for fixname in sorted(avail_fixes): - print(fixname) - if not args: - return 0 - if not args: - print("At least one file or directory argument required.", - file=sys.stderr) - print("Use --help to show usage.", file=sys.stderr) - return 2 - if "-" in args: - refactor_stdin = True - if options.write: - print("Can't write to stdin.", file=sys.stderr) - return 2 - - # Set up logging handler - level = logging.DEBUG if options.verbose else logging.INFO - logging.basicConfig(format='%(name)s: %(message)s', level=level) - - # Initialize the refactoring tool - unwanted_fixes = set(fixer_pkg + ".fix_" + fix for fix in options.nofix) - - extra_fixes = set() - if options.all_imports: - prefix = 'libpasteurize.fixes.' - extra_fixes.add(prefix + 'fix_add_all__future__imports') - extra_fixes.add(prefix + 'fix_add_future_standard_library_import') - extra_fixes.add(prefix + 'fix_add_all_future_builtins') - - fixer_names = avail_fixes | extra_fixes - unwanted_fixes - - rt = StdoutRefactoringTool(sorted(fixer_names), flags, set(), - options.nobackups, not options.no_diffs) - - # Refactor all files and directories passed as arguments - if not rt.errors: - if refactor_stdin: - rt.refactor_stdin() - else: - try: - rt.refactor(args, options.write, None, - options.processes) - except refactor.MultiprocessingUnsupported: - assert options.processes > 1 - print("Sorry, -j isn't " \ - "supported on this platform.", file=sys.stderr) - return 1 - rt.summarize() - - # Return error status (0 if rt.errors is zero) - return int(bool(rt.errors)) - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/builtins/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/builtins/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/builtins/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/builtins/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -""" -A resurrection of some old functions from Python 2 for use in Python 3. These -should be used sparingly, to help with porting efforts, since code using them -is no longer standard Python 3 code. - -This module provides the following: - -1. Implementations of these builtin functions which have no equivalent on Py3: - -- apply -- chr -- cmp -- execfile - -2. Aliases: - -- intern <- sys.intern -- raw_input <- input -- reduce <- functools.reduce -- reload <- imp.reload -- unichr <- chr -- unicode <- str -- xrange <- range - -3. List-producing versions of the corresponding Python 3 iterator-producing functions: - -- filter -- map -- range -- zip - -4. Forward-ported Py2 types: - -- basestring -- dict -- str -- long -- unicode - -""" - -from future.utils import PY3 -from past.builtins.noniterators import (filter, map, range, reduce, zip) -# from past.builtins.misc import (ascii, hex, input, oct, open) -if PY3: - from past.types import (basestring, - olddict as dict, - oldstr as str, - long, - unicode) -else: - from __builtin__ import (basestring, dict, str, long, unicode) - -from past.builtins.misc import (apply, chr, cmp, execfile, intern, oct, - raw_input, reload, unichr, unicode, xrange) -from past import utils - - -if utils.PY3: - # We only import names that shadow the builtins on Py3. No other namespace - # pollution on Py3. - - # Only shadow builtins on Py3; no new names - __all__ = ['filter', 'map', 'range', 'reduce', 'zip', - 'basestring', 'dict', 'str', 'long', 'unicode', - 'apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input', - 'reload', 'unichr', 'xrange' - ] - -else: - # No namespace pollution on Py2 - __all__ = [] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/builtins/misc.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/builtins/misc.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/builtins/misc.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/builtins/misc.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -from __future__ import unicode_literals -import sys -import inspect -from collections import Mapping - -from future.utils import PY3, exec_ - - -if PY3: - import builtins - - def apply(f, *args, **kw): - return f(*args, **kw) - - from past.builtins import str as oldstr - - def chr(i): - """ - Return a byte-string of one character with ordinal i; 0 <= i <= 256 - """ - return oldstr(bytes((i,))) - - def cmp(x, y): - """ - cmp(x, y) -> integer - - Return negative if xy. - """ - return (x > y) - (x < y) - - from sys import intern - - def oct(number): - """oct(number) -> string - - Return the octal representation of an integer - """ - return '0' + builtins.oct(number)[2:] - - raw_input = input - from imp import reload - unicode = str - unichr = chr - xrange = range -else: - import __builtin__ - apply = __builtin__.apply - chr = __builtin__.chr - cmp = __builtin__.cmp - execfile = __builtin__.execfile - intern = __builtin__.intern - oct = __builtin__.oct - raw_input = __builtin__.raw_input - reload = __builtin__.reload - unicode = __builtin__.unicode - unichr = __builtin__.unichr - xrange = __builtin__.xrange - - -if PY3: - def execfile(filename, myglobals=None, mylocals=None): - """ - Read and execute a Python script from a file in the given namespaces. - The globals and locals are dictionaries, defaulting to the current - globals and locals. If only globals is given, locals defaults to it. - """ - if myglobals is None: - # There seems to be no alternative to frame hacking here. - caller_frame = inspect.stack()[1] - myglobals = caller_frame[0].f_globals - mylocals = caller_frame[0].f_locals - elif mylocals is None: - # Only if myglobals is given do we set mylocals to it. - mylocals = myglobals - if not isinstance(myglobals, Mapping): - raise TypeError('globals must be a mapping') - if not isinstance(mylocals, Mapping): - raise TypeError('locals must be a mapping') - with open(filename, "rbU") as fin: - source = fin.read() - code = compile(source, filename, "exec") - exec_(code, myglobals, mylocals) - - -if PY3: - __all__ = ['apply', 'chr', 'cmp', 'execfile', 'intern', 'raw_input', - 'reload', 'unichr', 'unicode', 'xrange'] -else: - __all__ = [] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/builtins/noniterators.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/builtins/noniterators.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/builtins/noniterators.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/builtins/noniterators.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,273 +0,0 @@ -""" -This module is designed to be used as follows:: - - from past.builtins.noniterators import filter, map, range, reduce, zip - -And then, for example:: - - assert isinstance(range(5), list) - -The list-producing functions this brings in are:: - -- ``filter`` -- ``map`` -- ``range`` -- ``reduce`` -- ``zip`` - -""" - -from __future__ import division, absolute_import, print_function - -from itertools import chain, starmap -import itertools # since zip_longest doesn't exist on Py2 -from past.types import basestring -from past.utils import PY3 - - -def flatmap(f, items): - return chain.from_iterable(map(f, items)) - - -if PY3: - import builtins - - # list-producing versions of the major Python iterating functions - def oldfilter(*args): - """ - filter(function or None, sequence) -> list, tuple, or string - - Return those items of sequence for which function(item) is true. - If function is None, return the items that are true. If sequence - is a tuple or string, return the same type, else return a list. - """ - mytype = type(args[1]) - if isinstance(args[1], basestring): - return mytype().join(builtins.filter(*args)) - elif isinstance(args[1], (tuple, list)): - return mytype(builtins.filter(*args)) - else: - # Fall back to list. Is this the right thing to do? - return list(builtins.filter(*args)) - - # This is surprisingly difficult to get right. For example, the - # solutions here fail with the test cases in the docstring below: - # http://stackoverflow.com/questions/8072755/ - def oldmap(func, *iterables): - """ - map(function, sequence[, sequence, ...]) -> list - - Return a list of the results of applying the function to the - items of the argument sequence(s). If more than one sequence is - given, the function is called with an argument list consisting of - the corresponding item of each sequence, substituting None for - missing values when not all sequences have the same length. If - the function is None, return a list of the items of the sequence - (or a list of tuples if more than one sequence). - - Test cases: - >>> oldmap(None, 'hello world') - ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd'] - - >>> oldmap(None, range(4)) - [0, 1, 2, 3] - - More test cases are in past.tests.test_builtins. - """ - zipped = itertools.zip_longest(*iterables) - l = list(zipped) - if len(l) == 0: - return [] - if func is None: - result = l - else: - result = list(starmap(func, l)) - - # Inspect to see whether it's a simple sequence of tuples - try: - if max([len(item) for item in result]) == 1: - return list(chain.from_iterable(result)) - # return list(flatmap(func, result)) - except TypeError as e: - # Simple objects like ints have no len() - pass - return result - - ############################ - ### For reference, the source code for Py2.7 map function: - # static PyObject * - # builtin_map(PyObject *self, PyObject *args) - # { - # typedef struct { - # PyObject *it; /* the iterator object */ - # int saw_StopIteration; /* bool: did the iterator end? */ - # } sequence; - # - # PyObject *func, *result; - # sequence *seqs = NULL, *sqp; - # Py_ssize_t n, len; - # register int i, j; - # - # n = PyTuple_Size(args); - # if (n < 2) { - # PyErr_SetString(PyExc_TypeError, - # "map() requires at least two args"); - # return NULL; - # } - # - # func = PyTuple_GetItem(args, 0); - # n--; - # - # if (func == Py_None) { - # if (PyErr_WarnPy3k("map(None, ...) not supported in 3.x; " - # "use list(...)", 1) < 0) - # return NULL; - # if (n == 1) { - # /* map(None, S) is the same as list(S). */ - # return PySequence_List(PyTuple_GetItem(args, 1)); - # } - # } - # - # /* Get space for sequence descriptors. Must NULL out the iterator - # * pointers so that jumping to Fail_2 later doesn't see trash. - # */ - # if ((seqs = PyMem_NEW(sequence, n)) == NULL) { - # PyErr_NoMemory(); - # return NULL; - # } - # for (i = 0; i < n; ++i) { - # seqs[i].it = (PyObject*)NULL; - # seqs[i].saw_StopIteration = 0; - # } - # - # /* Do a first pass to obtain iterators for the arguments, and set len - # * to the largest of their lengths. - # */ - # len = 0; - # for (i = 0, sqp = seqs; i < n; ++i, ++sqp) { - # PyObject *curseq; - # Py_ssize_t curlen; - # - # /* Get iterator. */ - # curseq = PyTuple_GetItem(args, i+1); - # sqp->it = PyObject_GetIter(curseq); - # if (sqp->it == NULL) { - # static char errmsg[] = - # "argument %d to map() must support iteration"; - # char errbuf[sizeof(errmsg) + 25]; - # PyOS_snprintf(errbuf, sizeof(errbuf), errmsg, i+2); - # PyErr_SetString(PyExc_TypeError, errbuf); - # goto Fail_2; - # } - # - # /* Update len. */ - # curlen = _PyObject_LengthHint(curseq, 8); - # if (curlen > len) - # len = curlen; - # } - # - # /* Get space for the result list. */ - # if ((result = (PyObject *) PyList_New(len)) == NULL) - # goto Fail_2; - # - # /* Iterate over the sequences until all have stopped. */ - # for (i = 0; ; ++i) { - # PyObject *alist, *item=NULL, *value; - # int numactive = 0; - # - # if (func == Py_None && n == 1) - # alist = NULL; - # else if ((alist = PyTuple_New(n)) == NULL) - # goto Fail_1; - # - # for (j = 0, sqp = seqs; j < n; ++j, ++sqp) { - # if (sqp->saw_StopIteration) { - # Py_INCREF(Py_None); - # item = Py_None; - # } - # else { - # item = PyIter_Next(sqp->it); - # if (item) - # ++numactive; - # else { - # if (PyErr_Occurred()) { - # Py_XDECREF(alist); - # goto Fail_1; - # } - # Py_INCREF(Py_None); - # item = Py_None; - # sqp->saw_StopIteration = 1; - # } - # } - # if (alist) - # PyTuple_SET_ITEM(alist, j, item); - # else - # break; - # } - # - # if (!alist) - # alist = item; - # - # if (numactive == 0) { - # Py_DECREF(alist); - # break; - # } - # - # if (func == Py_None) - # value = alist; - # else { - # value = PyEval_CallObject(func, alist); - # Py_DECREF(alist); - # if (value == NULL) - # goto Fail_1; - # } - # if (i >= len) { - # int status = PyList_Append(result, value); - # Py_DECREF(value); - # if (status < 0) - # goto Fail_1; - # } - # else if (PyList_SetItem(result, i, value) < 0) - # goto Fail_1; - # } - # - # if (i < len && PyList_SetSlice(result, i, len, NULL) < 0) - # goto Fail_1; - # - # goto Succeed; - # - # Fail_1: - # Py_DECREF(result); - # Fail_2: - # result = NULL; - # Succeed: - # assert(seqs); - # for (i = 0; i < n; ++i) - # Py_XDECREF(seqs[i].it); - # PyMem_DEL(seqs); - # return result; - # } - - def oldrange(*args, **kwargs): - return list(builtins.range(*args, **kwargs)) - - def oldzip(*args, **kwargs): - return list(builtins.zip(*args, **kwargs)) - - filter = oldfilter - map = oldmap - range = oldrange - from functools import reduce - zip = oldzip - __all__ = ['filter', 'map', 'range', 'reduce', 'zip'] - -else: - import __builtin__ - # Python 2-builtin ranges produce lists - filter = __builtin__.filter - map = __builtin__.map - range = __builtin__.range - reduce = __builtin__.reduce - zip = __builtin__.zip - __all__ = [] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -# coding=utf-8 -""" -past: compatibility with Python 2 from Python 3 -=============================================== - -``past`` is a package to aid with Python 2/3 compatibility. Whereas ``future`` -contains backports of Python 3 constructs to Python 2, ``past`` provides -implementations of some Python 2 constructs in Python 3 and tools to import and -run Python 2 code in Python 3. It is intended to be used sparingly, as a way of -running old Python 2 code from Python 3 until the code is ported properly. - -Potential uses for libraries: - -- as a step in porting a Python 2 codebase to Python 3 (e.g. with the ``futurize`` script) -- to provide Python 3 support for previously Python 2-only libraries with the - same APIs as on Python 2 -- particularly with regard to 8-bit strings (the - ``past.builtins.str`` type). -- to aid in providing minimal-effort Python 3 support for applications using - libraries that do not yet wish to upgrade their code properly to Python 3, or - wish to upgrade it gradually to Python 3 style. - - -Here are some code examples that run identically on Python 3 and 2:: - - >>> from past.builtins import str as oldstr - - >>> philosopher = oldstr(u'\u5b54\u5b50'.encode('utf-8')) - >>> # This now behaves like a Py2 byte-string on both Py2 and Py3. - >>> # For example, indexing returns a Python 2-like string object, not - >>> # an integer: - >>> philosopher[0] - '\xe5' - >>> type(philosopher[0]) - - - >>> # List-producing versions of range, reduce, map, filter - >>> from past.builtins import range, reduce - >>> range(10) - [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] - >>> reduce(lambda x, y: x+y, [1, 2, 3, 4, 5]) - 15 - - >>> # Other functions removed in Python 3 are resurrected ... - >>> from past.builtins import execfile - >>> execfile('myfile.py') - - >>> from past.builtins import raw_input - >>> name = raw_input('What is your name? ') - What is your name? [cursor] - - >>> from past.builtins import reload - >>> reload(mymodule) # equivalent to imp.reload(mymodule) in Python 3 - - >>> from past.builtins import xrange - >>> for i in xrange(10): - ... pass - - -It also provides import hooks so you can import and use Python 2 modules like -this:: - - $ python3 - - >>> from past import autotranslate - >>> authotranslate('mypy2module') - >>> import mypy2module - -until the authors of the Python 2 modules have upgraded their code. Then, for -example:: - - >>> mypy2module.func_taking_py2_string(oldstr(b'abcd')) - - -Credits -------- - -:Author: Ed Schofield -:Sponsor: Python Charmers Pty Ltd, Australia: http://pythoncharmers.com - - -Licensing ---------- -Copyright 2013-2016 Python Charmers Pty Ltd, Australia. -The software is distributed under an MIT licence. See LICENSE.txt. -""" - - -from past.translation import install_hooks as autotranslate -from future import __version__, __copyright__, __license__ - -__title__ = 'past' -__author__ = 'Ed Schofield' - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/translation/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/translation/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/translation/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/translation/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,498 +0,0 @@ -# -*- coding: utf-8 -*- -""" -past.translation -================== - -The ``past.translation`` package provides an import hook for Python 3 which -transparently runs ``futurize`` fixers over Python 2 code on import to convert -print statements into functions, etc. - -It is intended to assist users in migrating to Python 3.x even if some -dependencies still only support Python 2.x. - -Usage ------ - -Once your Py2 package is installed in the usual module search path, the import -hook is invoked as follows: - - >>> from past import autotranslate - >>> autotranslate('mypackagename') - -Or: - - >>> autotranslate(['mypackage1', 'mypackage2']) - -You can unregister the hook using:: - - >>> from past.translation import remove_hooks - >>> remove_hooks() - -Author: Ed Schofield. -Inspired by and based on ``uprefix`` by Vinay M. Sajip. -""" - -import imp -import logging -import marshal -import os -import sys -import copy -from lib2to3.pgen2.parse import ParseError -from lib2to3.refactor import RefactoringTool - -from libfuturize import fixes - - -logger = logging.getLogger(__name__) -logger.setLevel(logging.DEBUG) - -myfixes = (list(fixes.libfuturize_fix_names_stage1) + - list(fixes.lib2to3_fix_names_stage1) + - list(fixes.libfuturize_fix_names_stage2) + - list(fixes.lib2to3_fix_names_stage2)) - - -# We detect whether the code is Py2 or Py3 by applying certain lib2to3 fixers -# to it. If the diff is empty, it's Python 3 code. - -py2_detect_fixers = [ -# From stage 1: - 'lib2to3.fixes.fix_apply', - # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. and move to stage2 - 'lib2to3.fixes.fix_except', - 'lib2to3.fixes.fix_execfile', - 'lib2to3.fixes.fix_exitfunc', - 'lib2to3.fixes.fix_funcattrs', - 'lib2to3.fixes.fix_filter', - 'lib2to3.fixes.fix_has_key', - 'lib2to3.fixes.fix_idioms', - 'lib2to3.fixes.fix_import', # makes any implicit relative imports explicit. (Use with ``from __future__ import absolute_import) - 'lib2to3.fixes.fix_intern', - 'lib2to3.fixes.fix_isinstance', - 'lib2to3.fixes.fix_methodattrs', - 'lib2to3.fixes.fix_ne', - 'lib2to3.fixes.fix_numliterals', # turns 1L into 1, 0755 into 0o755 - 'lib2to3.fixes.fix_paren', - 'lib2to3.fixes.fix_print', - 'lib2to3.fixes.fix_raise', # uses incompatible with_traceback() method on exceptions - 'lib2to3.fixes.fix_renames', - 'lib2to3.fixes.fix_reduce', - # 'lib2to3.fixes.fix_set_literal', # this is unnecessary and breaks Py2.6 support - 'lib2to3.fixes.fix_repr', - 'lib2to3.fixes.fix_standarderror', - 'lib2to3.fixes.fix_sys_exc', - 'lib2to3.fixes.fix_throw', - 'lib2to3.fixes.fix_tuple_params', - 'lib2to3.fixes.fix_types', - 'lib2to3.fixes.fix_ws_comma', - 'lib2to3.fixes.fix_xreadlines', - -# From stage 2: - 'lib2to3.fixes.fix_basestring', - # 'lib2to3.fixes.fix_buffer', # perhaps not safe. Test this. - # 'lib2to3.fixes.fix_callable', # not needed in Py3.2+ - # 'lib2to3.fixes.fix_dict', # TODO: add support for utils.viewitems() etc. - 'lib2to3.fixes.fix_exec', - # 'lib2to3.fixes.fix_future', # we don't want to remove __future__ imports - 'lib2to3.fixes.fix_getcwdu', - # 'lib2to3.fixes.fix_imports', # called by libfuturize.fixes.fix_future_standard_library - # 'lib2to3.fixes.fix_imports2', # we don't handle this yet (dbm) - # 'lib2to3.fixes.fix_input', - # 'lib2to3.fixes.fix_itertools', - # 'lib2to3.fixes.fix_itertools_imports', - 'lib2to3.fixes.fix_long', - # 'lib2to3.fixes.fix_map', - # 'lib2to3.fixes.fix_metaclass', # causes SyntaxError in Py2! Use the one from ``six`` instead - 'lib2to3.fixes.fix_next', - 'lib2to3.fixes.fix_nonzero', # TODO: add a decorator for mapping __bool__ to __nonzero__ - # 'lib2to3.fixes.fix_operator', # we will need support for this by e.g. extending the Py2 operator module to provide those functions in Py3 - 'lib2to3.fixes.fix_raw_input', - # 'lib2to3.fixes.fix_unicode', # strips off the u'' prefix, which removes a potentially helpful source of information for disambiguating unicode/byte strings - # 'lib2to3.fixes.fix_urllib', - 'lib2to3.fixes.fix_xrange', - # 'lib2to3.fixes.fix_zip', -] - - -class RTs: - """ - A namespace for the refactoring tools. This avoids creating these at - the module level, which slows down the module import. (See issue #117). - - There are two possible grammars: with or without the print statement. - Hence we have two possible refactoring tool implementations. - """ - _rt = None - _rtp = None - _rt_py2_detect = None - _rtp_py2_detect = None - - @staticmethod - def setup(): - """ - Call this before using the refactoring tools to create them on demand - if needed. - """ - if None in [RTs._rt, RTs._rtp]: - RTs._rt = RefactoringTool(myfixes) - RTs._rtp = RefactoringTool(myfixes, {'print_function': True}) - - - @staticmethod - def setup_detect_python2(): - """ - Call this before using the refactoring tools to create them on demand - if needed. - """ - if None in [RTs._rt_py2_detect, RTs._rtp_py2_detect]: - RTs._rt_py2_detect = RefactoringTool(py2_detect_fixers) - RTs._rtp_py2_detect = RefactoringTool(py2_detect_fixers, - {'print_function': True}) - - -# We need to find a prefix for the standard library, as we don't want to -# process any files there (they will already be Python 3). -# -# The following method is used by Sanjay Vinip in uprefix. This fails for -# ``conda`` environments: -# # In a non-pythonv virtualenv, sys.real_prefix points to the installed Python. -# # In a pythonv venv, sys.base_prefix points to the installed Python. -# # Outside a virtual environment, sys.prefix points to the installed Python. - -# if hasattr(sys, 'real_prefix'): -# _syslibprefix = sys.real_prefix -# else: -# _syslibprefix = getattr(sys, 'base_prefix', sys.prefix) - -# Instead, we use the portion of the path common to both the stdlib modules -# ``math`` and ``urllib``. - -def splitall(path): - """ - Split a path into all components. From Python Cookbook. - """ - allparts = [] - while True: - parts = os.path.split(path) - if parts[0] == path: # sentinel for absolute paths - allparts.insert(0, parts[0]) - break - elif parts[1] == path: # sentinel for relative paths - allparts.insert(0, parts[1]) - break - else: - path = parts[0] - allparts.insert(0, parts[1]) - return allparts - - -def common_substring(s1, s2): - """ - Returns the longest common substring to the two strings, starting from the - left. - """ - chunks = [] - path1 = splitall(s1) - path2 = splitall(s2) - for (dir1, dir2) in zip(path1, path2): - if dir1 != dir2: - break - chunks.append(dir1) - return os.path.join(*chunks) - -# _stdlibprefix = common_substring(math.__file__, urllib.__file__) - - -def detect_python2(source, pathname): - """ - Returns a bool indicating whether we think the code is Py2 - """ - RTs.setup_detect_python2() - try: - tree = RTs._rt_py2_detect.refactor_string(source, pathname) - except ParseError as e: - if e.msg != 'bad input' or e.value != '=': - raise - tree = RTs._rtp.refactor_string(source, pathname) - - if source != str(tree)[:-1]: # remove added newline - # The above fixers made changes, so we conclude it's Python 2 code - logger.debug('Detected Python 2 code: {0}'.format(pathname)) - with open('/tmp/original_code.py', 'w') as f: - f.write('### Original code (detected as py2): %s\n%s' % - (pathname, source)) - with open('/tmp/py2_detection_code.py', 'w') as f: - f.write('### Code after running py3 detection (from %s)\n%s' % - (pathname, str(tree)[:-1])) - return True - else: - logger.debug('Detected Python 3 code: {0}'.format(pathname)) - with open('/tmp/original_code.py', 'w') as f: - f.write('### Original code (detected as py3): %s\n%s' % - (pathname, source)) - try: - os.remove('/tmp/futurize_code.py') - except OSError: - pass - return False - - -class Py2Fixer(object): - """ - An import hook class that uses lib2to3 for source-to-source translation of - Py2 code to Py3. - """ - - # See the comments on :class:future.standard_library.RenameImport. - # We add this attribute here so remove_hooks() and install_hooks() can - # unambiguously detect whether the import hook is installed: - PY2FIXER = True - - def __init__(self): - self.found = None - self.base_exclude_paths = ['future', 'past'] - self.exclude_paths = copy.copy(self.base_exclude_paths) - self.include_paths = [] - - def include(self, paths): - """ - Pass in a sequence of module names such as 'plotrique.plotting' that, - if present at the leftmost side of the full package name, would - specify the module to be transformed from Py2 to Py3. - """ - self.include_paths += paths - - def exclude(self, paths): - """ - Pass in a sequence of strings such as 'mymodule' that, if - present at the leftmost side of the full package name, would cause - the module not to undergo any source transformation. - """ - self.exclude_paths += paths - - def find_module(self, fullname, path=None): - logger.debug('Running find_module: {0}...'.format(fullname)) - if '.' in fullname: - parent, child = fullname.rsplit('.', 1) - if path is None: - loader = self.find_module(parent, path) - mod = loader.load_module(parent) - path = mod.__path__ - fullname = child - - # Perhaps we should try using the new importlib functionality in Python - # 3.3: something like this? - # thing = importlib.machinery.PathFinder.find_module(fullname, path) - try: - self.found = imp.find_module(fullname, path) - except Exception as e: - logger.debug('Py2Fixer could not find {0}') - logger.debug('Exception was: {0})'.format(fullname, e)) - return None - self.kind = self.found[-1][-1] - if self.kind == imp.PKG_DIRECTORY: - self.pathname = os.path.join(self.found[1], '__init__.py') - elif self.kind == imp.PY_SOURCE: - self.pathname = self.found[1] - return self - - def transform(self, source): - # This implementation uses lib2to3, - # you can override and use something else - # if that's better for you - - # lib2to3 likes a newline at the end - RTs.setup() - source += '\n' - try: - tree = RTs._rt.refactor_string(source, self.pathname) - except ParseError as e: - if e.msg != 'bad input' or e.value != '=': - raise - tree = RTs._rtp.refactor_string(source, self.pathname) - # could optimise a bit for only doing str(tree) if - # getattr(tree, 'was_changed', False) returns True - return str(tree)[:-1] # remove added newline - - def load_module(self, fullname): - logger.debug('Running load_module for {0}...'.format(fullname)) - if fullname in sys.modules: - mod = sys.modules[fullname] - else: - if self.kind in (imp.PY_COMPILED, imp.C_EXTENSION, imp.C_BUILTIN, - imp.PY_FROZEN): - convert = False - # elif (self.pathname.startswith(_stdlibprefix) - # and 'site-packages' not in self.pathname): - # # We assume it's a stdlib package in this case. Is this too brittle? - # # Please file a bug report at https://github.com/PythonCharmers/python-future - # # if so. - # convert = False - # in theory, other paths could be configured to be excluded here too - elif any([fullname.startswith(path) for path in self.exclude_paths]): - convert = False - elif any([fullname.startswith(path) for path in self.include_paths]): - convert = True - else: - convert = False - if not convert: - logger.debug('Excluded {0} from translation'.format(fullname)) - mod = imp.load_module(fullname, *self.found) - else: - logger.debug('Autoconverting {0} ...'.format(fullname)) - mod = imp.new_module(fullname) - sys.modules[fullname] = mod - - # required by PEP 302 - mod.__file__ = self.pathname - mod.__name__ = fullname - mod.__loader__ = self - - # This: - # mod.__package__ = '.'.join(fullname.split('.')[:-1]) - # seems to result in "SystemError: Parent module '' not loaded, - # cannot perform relative import" for a package's __init__.py - # file. We use the approach below. Another option to try is the - # minimal load_module pattern from the PEP 302 text instead. - - # Is the test in the next line more or less robust than the - # following one? Presumably less ... - # ispkg = self.pathname.endswith('__init__.py') - - if self.kind == imp.PKG_DIRECTORY: - mod.__path__ = [ os.path.dirname(self.pathname) ] - mod.__package__ = fullname - else: - #else, regular module - mod.__path__ = [] - mod.__package__ = fullname.rpartition('.')[0] - - try: - cachename = imp.cache_from_source(self.pathname) - if not os.path.exists(cachename): - update_cache = True - else: - sourcetime = os.stat(self.pathname).st_mtime - cachetime = os.stat(cachename).st_mtime - update_cache = cachetime < sourcetime - # # Force update_cache to work around a problem with it being treated as Py3 code??? - # update_cache = True - if not update_cache: - with open(cachename, 'rb') as f: - data = f.read() - try: - code = marshal.loads(data) - except Exception: - # pyc could be corrupt. Regenerate it - update_cache = True - if update_cache: - if self.found[0]: - source = self.found[0].read() - elif self.kind == imp.PKG_DIRECTORY: - with open(self.pathname) as f: - source = f.read() - - if detect_python2(source, self.pathname): - source = self.transform(source) - with open('/tmp/futurized_code.py', 'w') as f: - f.write('### Futurized code (from %s)\n%s' % - (self.pathname, source)) - - code = compile(source, self.pathname, 'exec') - - dirname = os.path.dirname(cachename) - if not os.path.exists(dirname): - os.makedirs(dirname) - try: - with open(cachename, 'wb') as f: - data = marshal.dumps(code) - f.write(data) - except Exception: # could be write-protected - pass - exec(code, mod.__dict__) - except Exception as e: - # must remove module from sys.modules - del sys.modules[fullname] - raise # keep it simple - - if self.found[0]: - self.found[0].close() - return mod - -_hook = Py2Fixer() - - -def install_hooks(include_paths=(), exclude_paths=()): - if isinstance(include_paths, str): - include_paths = (include_paths,) - if isinstance(exclude_paths, str): - exclude_paths = (exclude_paths,) - assert len(include_paths) + len(exclude_paths) > 0, 'Pass at least one argument' - _hook.include(include_paths) - _hook.exclude(exclude_paths) - # _hook.debug = debug - enable = sys.version_info[0] >= 3 # enabled for all 3.x - if enable and _hook not in sys.meta_path: - sys.meta_path.insert(0, _hook) # insert at beginning. This could be made a parameter - - # We could return the hook when there are ways of configuring it - #return _hook - - -def remove_hooks(): - if _hook in sys.meta_path: - sys.meta_path.remove(_hook) - - -def detect_hooks(): - """ - Returns True if the import hooks are installed, False if not. - """ - return _hook in sys.meta_path - # present = any([hasattr(hook, 'PY2FIXER') for hook in sys.meta_path]) - # return present - - -class hooks(object): - """ - Acts as a context manager. Use like this: - - >>> from past import translation - >>> with translation.hooks(): - ... import mypy2module - >>> import requests # py2/3 compatible anyway - >>> # etc. - """ - def __enter__(self): - self.hooks_were_installed = detect_hooks() - install_hooks() - return self - - def __exit__(self, *args): - if not self.hooks_were_installed: - remove_hooks() - - -class suspend_hooks(object): - """ - Acts as a context manager. Use like this: - - >>> from past import translation - >>> translation.install_hooks() - >>> import http.client - >>> # ... - >>> with translation.suspend_hooks(): - >>> import requests # or others that support Py2/3 - - If the hooks were disabled before the context, they are not installed when - the context is left. - """ - def __enter__(self): - self.hooks_were_installed = detect_hooks() - remove_hooks() - return self - def __exit__(self, *args): - if self.hooks_were_installed: - install_hooks() - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/basestring.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/basestring.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/basestring.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/basestring.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -""" -An implementation of the basestring type for Python 3 - -Example use: - ->>> s = b'abc' ->>> assert isinstance(s, basestring) ->>> from past.types import str as oldstr ->>> s2 = oldstr(b'abc') ->>> assert isinstance(s2, basestring) - -""" - -import sys - -from past.utils import with_metaclass, PY2 - -if PY2: - str = unicode - -ver = sys.version_info[:2] - - -class BaseBaseString(type): - def __instancecheck__(cls, instance): - return isinstance(instance, (bytes, str)) - - def __subclasshook__(cls, thing): - # TODO: What should go here? - raise NotImplemented - - -class basestring(with_metaclass(BaseBaseString)): - """ - A minimal backport of the Python 2 basestring type to Py3 - """ - - -__all__ = ['basestring'] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -""" -Forward-ports of types from Python 2 for use with Python 3: - -- ``basestring``: equivalent to ``(str, bytes)`` in ``isinstance`` checks -- ``dict``: with list-producing .keys() etc. methods -- ``str``: bytes-like, but iterating over them doesn't product integers -- ``long``: alias of Py3 int with ``L`` suffix in the ``repr`` -- ``unicode``: alias of Py3 str with ``u`` prefix in the ``repr`` - -""" - -from past import utils - -if utils.PY2: - import __builtin__ - basestring = __builtin__.basestring - dict = __builtin__.dict - str = __builtin__.str - long = __builtin__.long - unicode = __builtin__.unicode - __all__ = [] -else: - from .basestring import basestring - from .olddict import olddict - from .oldstr import oldstr - long = int - unicode = str - # from .unicode import unicode - __all__ = ['basestring', 'olddict', 'oldstr', 'long', 'unicode'] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/olddict.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/olddict.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/olddict.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/olddict.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -""" -A dict subclass for Python 3 that behaves like Python 2's dict - -Example use: - ->>> from past.builtins import dict ->>> d1 = dict() # instead of {} for an empty dict ->>> d2 = dict(key1='value1', key2='value2') - -The keys, values and items methods now return lists on Python 3.x and there are -methods for iterkeys, itervalues, iteritems, and viewkeys etc. - ->>> for d in (d1, d2): -... assert isinstance(d.keys(), list) -... assert isinstance(d.values(), list) -... assert isinstance(d.items(), list) -""" - -import sys - -from past.utils import with_metaclass - - -_builtin_dict = dict -ver = sys.version_info[:2] - - -class BaseOldDict(type): - def __instancecheck__(cls, instance): - return isinstance(instance, _builtin_dict) - - -class olddict(with_metaclass(BaseOldDict, _builtin_dict)): - """ - A backport of the Python 3 dict object to Py2 - """ - iterkeys = _builtin_dict.keys - viewkeys = _builtin_dict.keys - - def keys(self): - return list(super(olddict, self).keys()) - - itervalues = _builtin_dict.values - viewvalues = _builtin_dict.values - - def values(self): - return list(super(olddict, self).values()) - - iteritems = _builtin_dict.items - viewitems = _builtin_dict.items - - def items(self): - return list(super(olddict, self).items()) - - def has_key(self, k): - """ - D.has_key(k) -> True if D has a key k, else False - """ - return k in self - - # def __new__(cls, *args, **kwargs): - # """ - # dict() -> new empty dictionary - # dict(mapping) -> new dictionary initialized from a mapping object's - # (key, value) pairs - # dict(iterable) -> new dictionary initialized as if via: - # d = {} - # for k, v in iterable: - # d[k] = v - # dict(**kwargs) -> new dictionary initialized with the name=value pairs - # in the keyword argument list. For example: dict(one=1, two=2) - - # """ - # - # if len(args) == 0: - # return super(olddict, cls).__new__(cls) - # # Was: elif isinstance(args[0], newbytes): - # # We use type() instead of the above because we're redefining - # # this to be True for all unicode string subclasses. Warning: - # # This may render newstr un-subclassable. - # elif type(args[0]) == olddict: - # return args[0] - # # elif isinstance(args[0], _builtin_dict): - # # value = args[0] - # else: - # value = args[0] - # return super(olddict, cls).__new__(cls, value) - - def __native__(self): - """ - Hook for the past.utils.native() function - """ - return super(oldbytes, self) - - -__all__ = ['olddict'] - diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/oldstr.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/oldstr.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/types/oldstr.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/types/oldstr.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -""" -Pure-Python implementation of a Python 2-like str object for Python 3. -""" - -from collections import Iterable -from numbers import Integral - -from past.utils import PY2, with_metaclass - - -_builtin_bytes = bytes - - -class BaseOldStr(type): - def __instancecheck__(cls, instance): - return isinstance(instance, _builtin_bytes) - - -def unescape(s): - """ - Interprets strings with escape sequences - - Example: - >>> s = unescape(r'abc\\def') # i.e. 'abc\\\\def' - >>> print(s) - 'abc\def' - >>> s2 = unescape('abc\\ndef') - >>> len(s2) - 8 - >>> print(s2) - abc - def - """ - return s.encode().decode('unicode_escape') - - -class oldstr(with_metaclass(BaseOldStr, _builtin_bytes)): - """ - A forward port of the Python 2 8-bit string object to Py3 - """ - # Python 2 strings have no __iter__ method: - @property - def __iter__(self): - raise AttributeError - - def __dir__(self): - return [thing for thing in dir(_builtin_bytes) if thing != '__iter__'] - - # def __new__(cls, *args, **kwargs): - # """ - # From the Py3 bytes docstring: - - # bytes(iterable_of_ints) -> bytes - # bytes(string, encoding[, errors]) -> bytes - # bytes(bytes_or_buffer) -> immutable copy of bytes_or_buffer - # bytes(int) -> bytes object of size given by the parameter initialized with null bytes - # bytes() -> empty bytes object - # - # Construct an immutable array of bytes from: - # - an iterable yielding integers in range(256) - # - a text string encoded using the specified encoding - # - any object implementing the buffer API. - # - an integer - # """ - # - # if len(args) == 0: - # return super(newbytes, cls).__new__(cls) - # # Was: elif isinstance(args[0], newbytes): - # # We use type() instead of the above because we're redefining - # # this to be True for all unicode string subclasses. Warning: - # # This may render newstr un-subclassable. - # elif type(args[0]) == newbytes: - # return args[0] - # elif isinstance(args[0], _builtin_bytes): - # value = args[0] - # elif isinstance(args[0], unicode): - # if 'encoding' not in kwargs: - # raise TypeError('unicode string argument without an encoding') - # ### - # # Was: value = args[0].encode(**kwargs) - # # Python 2.6 string encode() method doesn't take kwargs: - # # Use this instead: - # newargs = [kwargs['encoding']] - # if 'errors' in kwargs: - # newargs.append(kwargs['errors']) - # value = args[0].encode(*newargs) - # ### - # elif isinstance(args[0], Iterable): - # if len(args[0]) == 0: - # # What is this? - # raise ValueError('unknown argument type') - # elif len(args[0]) > 0 and isinstance(args[0][0], Integral): - # # It's a list of integers - # value = b''.join([chr(x) for x in args[0]]) - # else: - # raise ValueError('item cannot be interpreted as an integer') - # elif isinstance(args[0], Integral): - # if args[0] < 0: - # raise ValueError('negative count') - # value = b'\x00' * args[0] - # else: - # value = args[0] - # return super(newbytes, cls).__new__(cls, value) - - def __repr__(self): - s = super(oldstr, self).__repr__() # e.g. b'abc' on Py3, b'abc' on Py3 - return s[1:] - - def __str__(self): - s = super(oldstr, self).__str__() # e.g. "b'abc'" or "b'abc\\ndef' - # TODO: fix this: - assert s[:2] == "b'" and s[-1] == "'" - return unescape(s[2:-1]) # e.g. 'abc' or 'abc\ndef' - - def __getitem__(self, y): - if isinstance(y, Integral): - return super(oldstr, self).__getitem__(slice(y, y+1)) - else: - return super(oldstr, self).__getitem__(y) - - def __getslice__(self, *args): - return self.__getitem__(slice(*args)) - - def __contains__(self, key): - if isinstance(key, int): - return False - - def __native__(self): - return bytes(self) - - -__all__ = ['oldstr'] diff -Nru pyglet-1.4.10/tests/extlibs/future/py2_3/past/utils/__init__.py pyglet-1.5.14/tests/extlibs/future/py2_3/past/utils/__init__.py --- pyglet-1.4.10/tests/extlibs/future/py2_3/past/utils/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/future/py2_3/past/utils/__init__.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -""" -Various non-built-in utility functions and definitions for Py2 -compatibility in Py3. - -For example: - - >>> # The old_div() function behaves like Python 2's / operator - >>> # without "from __future__ import division" - >>> from past.utils import old_div - >>> old_div(3, 2) # like 3/2 in Py2 - 0 - >>> old_div(3, 2.0) # like 3/2.0 in Py2 - 1.5 -""" - -import sys -import numbers - -PY3 = sys.version_info[0] == 3 -PY2 = sys.version_info[0] == 2 -PYPY = hasattr(sys, 'pypy_translation_info') - - -def with_metaclass(meta, *bases): - """ - Function from jinja2/_compat.py. License: BSD. - - Use it like this:: - - class BaseForm(object): - pass - - class FormType(type): - pass - - class Form(with_metaclass(FormType, BaseForm)): - pass - - 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. Because of internal type checks - we also need to make sure that we downgrade the custom metaclass - for one level to something closer to type (that's why __call__ and - __init__ comes back from type etc.). - - This has the advantage over six.with_metaclass of not introducing - dummy classes into the final MRO. - """ - class metaclass(meta): - __call__ = type.__call__ - __init__ = type.__init__ - def __new__(cls, name, this_bases, d): - if this_bases is None: - return type.__new__(cls, name, (), d) - return meta(name, bases, d) - return metaclass('temporary_class', None, {}) - - -def native(obj): - """ - On Py2, this is a no-op: native(obj) -> obj - - On Py3, returns the corresponding native Py3 types that are - superclasses for forward-ported objects from Py2: - - >>> from past.builtins import str, dict - - >>> native(str(b'ABC')) # Output on Py3 follows. On Py2, output is 'ABC' - b'ABC' - >>> type(native(str(b'ABC'))) - bytes - - Existing native types on Py3 will be returned unchanged: - - >>> type(native(b'ABC')) - bytes - """ - if hasattr(obj, '__native__'): - return obj.__native__() - else: - return obj - - -# An alias for future.utils.old_div(): -def old_div(a, b): - """ - Equivalent to ``a / b`` on Python 2 without ``from __future__ import - division``. - - TODO: generalize this to other objects (like arrays etc.) - """ - if isinstance(a, numbers.Integral) and isinstance(b, numbers.Integral): - return a // b - else: - return a / b - -__all__ = ['PY3', 'PY2', 'PYPY', 'with_metaclass', 'native', 'old_div'] diff -Nru pyglet-1.4.10/tests/extlibs/mock.py pyglet-1.5.14/tests/extlibs/mock.py --- pyglet-1.4.10/tests/extlibs/mock.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/tests/extlibs/mock.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,2367 +0,0 @@ -# mock.py -# Test tools for mocking and patching. -# Copyright (C) 2007-2012 Michael Foord & the mock team -# E-mail: fuzzyman AT voidspace DOT org DOT uk - -# mock 1.0 -# http://www.voidspace.org.uk/python/mock/ - -# Released subject to the BSD License -# Please see http://www.voidspace.org.uk/python/license.shtml - -# Scripts maintained at http://www.voidspace.org.uk/python/index.shtml -# Comments, suggestions and bug reports welcome. - - -__all__ = ( - 'Mock', - 'MagicMock', - 'patch', - 'sentinel', - 'DEFAULT', - 'ANY', - 'call', - 'create_autospec', - 'FILTER_DIR', - 'NonCallableMock', - 'NonCallableMagicMock', - 'mock_open', - 'PropertyMock', -) - - -__version__ = '1.0.1' - - -import pprint -import sys - -try: - import inspect -except ImportError: - # for alternative platforms that - # may not have inspect - inspect = None - -try: - from functools import wraps as original_wraps -except ImportError: - # Python 2.4 compatibility - def wraps(original): - def inner(f): - f.__name__ = original.__name__ - f.__doc__ = original.__doc__ - f.__module__ = original.__module__ - f.__wrapped__ = original - return f - return inner -else: - if sys.version_info[:2] >= (3, 3): - wraps = original_wraps - else: - def wraps(func): - def inner(f): - f = original_wraps(func)(f) - f.__wrapped__ = func - return f - return inner - -try: - unicode -except NameError: - # Python 3 - basestring = unicode = str - -try: - long -except NameError: - # Python 3 - long = int - -try: - BaseException -except NameError: - # Python 2.4 compatibility - BaseException = Exception - -try: - next -except NameError: - def next(obj): - return obj.next() - - -BaseExceptions = (BaseException,) -if 'java' in sys.platform: - # jython - import java - BaseExceptions = (BaseException, java.lang.Throwable) - -try: - _isidentifier = str.isidentifier -except AttributeError: - # Python 2.X - import keyword - import re - regex = re.compile(r'^[a-z_][a-z0-9_]*$', re.I) - def _isidentifier(string): - if string in keyword.kwlist: - return False - return regex.match(string) - - -inPy3k = sys.version_info[0] == 3 - -# Needed to work around Python 3 bug where use of "super" interferes with -# defining __class__ as a descriptor -_super = super - -self = 'im_self' -builtin = '__builtin__' -if inPy3k: - self = '__self__' - builtin = 'builtins' - -FILTER_DIR = True - - -def _is_instance_mock(obj): - # can't use isinstance on Mock objects because they override __class__ - # The base class for all mocks is NonCallableMock - return issubclass(type(obj), NonCallableMock) - - -def _is_exception(obj): - return ( - isinstance(obj, BaseExceptions) or - isinstance(obj, ClassTypes) and issubclass(obj, BaseExceptions) - ) - - -class _slotted(object): - __slots__ = ['a'] - - -DescriptorTypes = ( - type(_slotted.a), - property, -) - - -def _getsignature(func, skipfirst, instance=False): - if inspect is None: - raise ImportError('inspect module not available') - - if isinstance(func, ClassTypes) and not instance: - try: - func = func.__init__ - except AttributeError: - return - skipfirst = True - elif not isinstance(func, FunctionTypes): - # for classes where instance is True we end up here too - try: - func = func.__call__ - except AttributeError: - return - - if inPy3k: - try: - argspec = inspect.getfullargspec(func) - except TypeError: - # C function / method, possibly inherited object().__init__ - return - regargs, varargs, varkw, defaults, kwonly, kwonlydef, ann = argspec - else: - try: - regargs, varargs, varkwargs, defaults = inspect.getargspec(func) - except TypeError: - # C function / method, possibly inherited object().__init__ - return - - # instance methods and classmethods need to lose the self argument - if getattr(func, self, None) is not None: - regargs = regargs[1:] - if skipfirst: - # this condition and the above one are never both True - why? - regargs = regargs[1:] - - if inPy3k: - signature = inspect.formatargspec( - regargs, varargs, varkw, defaults, - kwonly, kwonlydef, ann, formatvalue=lambda value: "") - else: - signature = inspect.formatargspec( - regargs, varargs, varkwargs, defaults, - formatvalue=lambda value: "") - return signature[1:-1], func - - -def _check_signature(func, mock, skipfirst, instance=False): - if not _callable(func): - return - - result = _getsignature(func, skipfirst, instance) - if result is None: - return - signature, func = result - - # can't use self because "self" is common as an argument name - # unfortunately even not in the first place - src = "lambda _mock_self, %s: None" % signature - checksig = eval(src, {}) - _copy_func_details(func, checksig) - type(mock)._mock_check_sig = checksig - - -def _copy_func_details(func, funcopy): - funcopy.__name__ = func.__name__ - funcopy.__doc__ = func.__doc__ - #funcopy.__dict__.update(func.__dict__) - funcopy.__module__ = func.__module__ - if not inPy3k: - funcopy.func_defaults = func.func_defaults - return - funcopy.__defaults__ = func.__defaults__ - funcopy.__kwdefaults__ = func.__kwdefaults__ - - -def _callable(obj): - if isinstance(obj, ClassTypes): - return True - if getattr(obj, '__call__', None) is not None: - return True - return False - - -def _is_list(obj): - # checks for list or tuples - # XXXX badly named! - return type(obj) in (list, tuple) - - -def _instance_callable(obj): - """Given an object, return True if the object is callable. - For classes, return True if instances would be callable.""" - if not isinstance(obj, ClassTypes): - # already an instance - return getattr(obj, '__call__', None) is not None - - klass = obj - # uses __bases__ instead of __mro__ so that we work with old style classes - if klass.__dict__.get('__call__') is not None: - return True - - for base in klass.__bases__: - if _instance_callable(base): - return True - return False - - -def _set_signature(mock, original, instance=False): - # creates a function with signature (*args, **kwargs) that delegates to a - # mock. It still does signature checking by calling a lambda with the same - # signature as the original. - if not _callable(original): - return - - skipfirst = isinstance(original, ClassTypes) - result = _getsignature(original, skipfirst, instance) - if result is None: - # was a C function (e.g. object().__init__ ) that can't be mocked - return - - signature, func = result - - src = "lambda %s: None" % signature - checksig = eval(src, {}) - _copy_func_details(func, checksig) - - name = original.__name__ - if not _isidentifier(name): - name = 'funcopy' - context = {'_checksig_': checksig, 'mock': mock} - src = """def %s(*args, **kwargs): - _checksig_(*args, **kwargs) - return mock(*args, **kwargs)""" % name - exec (src, context) - funcopy = context[name] - _setup_func(funcopy, mock) - return funcopy - - -def _setup_func(funcopy, mock): - funcopy.mock = mock - - # can't use isinstance with mocks - if not _is_instance_mock(mock): - return - - def assert_called_with(*args, **kwargs): - return mock.assert_called_with(*args, **kwargs) - def assert_called_once_with(*args, **kwargs): - return mock.assert_called_once_with(*args, **kwargs) - def assert_has_calls(*args, **kwargs): - return mock.assert_has_calls(*args, **kwargs) - def assert_any_call(*args, **kwargs): - return mock.assert_any_call(*args, **kwargs) - def reset_mock(): - funcopy.method_calls = _CallList() - funcopy.mock_calls = _CallList() - mock.reset_mock() - ret = funcopy.return_value - if _is_instance_mock(ret) and not ret is mock: - ret.reset_mock() - - funcopy.called = False - funcopy.call_count = 0 - funcopy.call_args = None - funcopy.call_args_list = _CallList() - funcopy.method_calls = _CallList() - funcopy.mock_calls = _CallList() - - funcopy.return_value = mock.return_value - funcopy.side_effect = mock.side_effect - funcopy._mock_children = mock._mock_children - - funcopy.assert_called_with = assert_called_with - funcopy.assert_called_once_with = assert_called_once_with - funcopy.assert_has_calls = assert_has_calls - funcopy.assert_any_call = assert_any_call - funcopy.reset_mock = reset_mock - - mock._mock_delegate = funcopy - - -def _is_magic(name): - return '__%s__' % name[2:-2] == name - - -class _SentinelObject(object): - "A unique, named, sentinel object." - def __init__(self, name): - self.name = name - - def __repr__(self): - return 'sentinel.%s' % self.name - - -class _Sentinel(object): - """Access attributes to return a named object, usable as a sentinel.""" - def __init__(self): - self._sentinels = {} - - def __getattr__(self, name): - if name == '__bases__': - # Without this help(mock) raises an exception - raise AttributeError - return self._sentinels.setdefault(name, _SentinelObject(name)) - - -sentinel = _Sentinel() - -DEFAULT = sentinel.DEFAULT -_missing = sentinel.MISSING -_deleted = sentinel.DELETED - - -class OldStyleClass: - pass -ClassType = type(OldStyleClass) - - -def _copy(value): - if type(value) in (dict, list, tuple, set): - return type(value)(value) - return value - - -ClassTypes = (type,) -if not inPy3k: - ClassTypes = (type, ClassType) - -_allowed_names = set( - [ - 'return_value', '_mock_return_value', 'side_effect', - '_mock_side_effect', '_mock_parent', '_mock_new_parent', - '_mock_name', '_mock_new_name' - ] -) - - -def _delegating_property(name): - _allowed_names.add(name) - _the_name = '_mock_' + name - def _get(self, name=name, _the_name=_the_name): - sig = self._mock_delegate - if sig is None: - return getattr(self, _the_name) - return getattr(sig, name) - def _set(self, value, name=name, _the_name=_the_name): - sig = self._mock_delegate - if sig is None: - self.__dict__[_the_name] = value - else: - setattr(sig, name, value) - - return property(_get, _set) - - - -class _CallList(list): - - def __contains__(self, value): - if not isinstance(value, list): - return list.__contains__(self, value) - len_value = len(value) - len_self = len(self) - if len_value > len_self: - return False - - for i in range(0, len_self - len_value + 1): - sub_list = self[i:i+len_value] - if sub_list == value: - return True - return False - - def __repr__(self): - return pprint.pformat(list(self)) - - -def _check_and_set_parent(parent, value, name, new_name): - if not _is_instance_mock(value): - return False - if ((value._mock_name or value._mock_new_name) or - (value._mock_parent is not None) or - (value._mock_new_parent is not None)): - return False - - _parent = parent - while _parent is not None: - # setting a mock (value) as a child or return value of itself - # should not modify the mock - if _parent is value: - return False - _parent = _parent._mock_new_parent - - if new_name: - value._mock_new_parent = parent - value._mock_new_name = new_name - if name: - value._mock_parent = parent - value._mock_name = name - return True - - - -class Base(object): - _mock_return_value = DEFAULT - _mock_side_effect = None - def __init__(self, *args, **kwargs): - pass - - - -class NonCallableMock(Base): - """A non-callable version of `Mock`""" - - def __new__(cls, *args, **kw): - # every instance has its own class - # so we can create magic methods on the - # class without stomping on other mocks - new = type(cls.__name__, (cls,), {'__doc__': cls.__doc__}) - instance = object.__new__(new) - return instance - - - def __init__( - self, spec=None, wraps=None, name=None, spec_set=None, - parent=None, _spec_state=None, _new_name='', _new_parent=None, - **kwargs - ): - if _new_parent is None: - _new_parent = parent - - __dict__ = self.__dict__ - __dict__['_mock_parent'] = parent - __dict__['_mock_name'] = name - __dict__['_mock_new_name'] = _new_name - __dict__['_mock_new_parent'] = _new_parent - - if spec_set is not None: - spec = spec_set - spec_set = True - - self._mock_add_spec(spec, spec_set) - - __dict__['_mock_children'] = {} - __dict__['_mock_wraps'] = wraps - __dict__['_mock_delegate'] = None - - __dict__['_mock_called'] = False - __dict__['_mock_call_args'] = None - __dict__['_mock_call_count'] = 0 - __dict__['_mock_call_args_list'] = _CallList() - __dict__['_mock_mock_calls'] = _CallList() - - __dict__['method_calls'] = _CallList() - - if kwargs: - self.configure_mock(**kwargs) - - _super(NonCallableMock, self).__init__( - spec, wraps, name, spec_set, parent, - _spec_state - ) - - - def attach_mock(self, mock, attribute): - """ - Attach a mock as an attribute of this one, replacing its name and - parent. Calls to the attached mock will be recorded in the - `method_calls` and `mock_calls` attributes of this one.""" - mock._mock_parent = None - mock._mock_new_parent = None - mock._mock_name = '' - mock._mock_new_name = None - - setattr(self, attribute, mock) - - - def mock_add_spec(self, spec, spec_set=False): - """Add a spec to a mock. `spec` can either be an object or a - list of strings. Only attributes on the `spec` can be fetched as - attributes from the mock. - - If `spec_set` is True then only attributes on the spec can be set.""" - self._mock_add_spec(spec, spec_set) - - - def _mock_add_spec(self, spec, spec_set): - _spec_class = None - - if spec is not None and not _is_list(spec): - if isinstance(spec, ClassTypes): - _spec_class = spec - else: - _spec_class = _get_class(spec) - - spec = dir(spec) - - __dict__ = self.__dict__ - __dict__['_spec_class'] = _spec_class - __dict__['_spec_set'] = spec_set - __dict__['_mock_methods'] = spec - - - def __get_return_value(self): - ret = self._mock_return_value - if self._mock_delegate is not None: - ret = self._mock_delegate.return_value - - if ret is DEFAULT: - ret = self._get_child_mock( - _new_parent=self, _new_name='()' - ) - self.return_value = ret - return ret - - - def __set_return_value(self, value): - if self._mock_delegate is not None: - self._mock_delegate.return_value = value - else: - self._mock_return_value = value - _check_and_set_parent(self, value, None, '()') - - __return_value_doc = "The value to be returned when the mock is called." - return_value = property(__get_return_value, __set_return_value, - __return_value_doc) - - - @property - def __class__(self): - if self._spec_class is None: - return type(self) - return self._spec_class - - called = _delegating_property('called') - call_count = _delegating_property('call_count') - call_args = _delegating_property('call_args') - call_args_list = _delegating_property('call_args_list') - mock_calls = _delegating_property('mock_calls') - - - def __get_side_effect(self): - sig = self._mock_delegate - if sig is None: - return self._mock_side_effect - return sig.side_effect - - def __set_side_effect(self, value): - value = _try_iter(value) - sig = self._mock_delegate - if sig is None: - self._mock_side_effect = value - else: - sig.side_effect = value - - side_effect = property(__get_side_effect, __set_side_effect) - - - def reset_mock(self): - "Restore the mock object to its initial state." - self.called = False - self.call_args = None - self.call_count = 0 - self.mock_calls = _CallList() - self.call_args_list = _CallList() - self.method_calls = _CallList() - - for child in self._mock_children.values(): - if isinstance(child, _SpecState): - continue - child.reset_mock() - - ret = self._mock_return_value - if _is_instance_mock(ret) and ret is not self: - ret.reset_mock() - - - def configure_mock(self, **kwargs): - """Set attributes on the mock through keyword arguments. - - Attributes plus return values and side effects can be set on child - mocks using standard dot notation and unpacking a dictionary in the - method call: - - >>> attrs = {'method.return_value': 3, 'other.side_effect': KeyError} - >>> mock.configure_mock(**attrs)""" - for arg, val in sorted(kwargs.items(), - # we sort on the number of dots so that - # attributes are set before we set attributes on - # attributes - key=lambda entry: entry[0].count('.')): - args = arg.split('.') - final = args.pop() - obj = self - for entry in args: - obj = getattr(obj, entry) - setattr(obj, final, val) - - - def __getattr__(self, name): - if name == '_mock_methods': - raise AttributeError(name) - elif self._mock_methods is not None: - if name not in self._mock_methods or name in _all_magics: - raise AttributeError("Mock object has no attribute %r" % name) - elif _is_magic(name): - raise AttributeError(name) - - result = self._mock_children.get(name) - if result is _deleted: - raise AttributeError(name) - elif result is None: - wraps = None - if self._mock_wraps is not None: - # XXXX should we get the attribute without triggering code - # execution? - wraps = getattr(self._mock_wraps, name) - - result = self._get_child_mock( - parent=self, name=name, wraps=wraps, _new_name=name, - _new_parent=self - ) - self._mock_children[name] = result - - elif isinstance(result, _SpecState): - result = create_autospec( - result.spec, result.spec_set, result.instance, - result.parent, result.name - ) - self._mock_children[name] = result - - return result - - - def __repr__(self): - _name_list = [self._mock_new_name] - _parent = self._mock_new_parent - last = self - - dot = '.' - if _name_list == ['()']: - dot = '' - seen = set() - while _parent is not None: - last = _parent - - _name_list.append(_parent._mock_new_name + dot) - dot = '.' - if _parent._mock_new_name == '()': - dot = '' - - _parent = _parent._mock_new_parent - - # use ids here so as not to call __hash__ on the mocks - if id(_parent) in seen: - break - seen.add(id(_parent)) - - _name_list = list(reversed(_name_list)) - _first = last._mock_name or 'mock' - if len(_name_list) > 1: - if _name_list[1] not in ('()', '().'): - _first += '.' - _name_list[0] = _first - name = ''.join(_name_list) - - name_string = '' - if name not in ('mock', 'mock.'): - name_string = ' name=%r' % name - - spec_string = '' - if self._spec_class is not None: - spec_string = ' spec=%r' - if self._spec_set: - spec_string = ' spec_set=%r' - spec_string = spec_string % self._spec_class.__name__ - return "<%s%s%s id='%s'>" % ( - type(self).__name__, - name_string, - spec_string, - id(self) - ) - - - def __dir__(self): - """Filter the output of `dir(mock)` to only useful members. - XXXX - """ - extras = self._mock_methods or [] - from_type = dir(type(self)) - from_dict = list(self.__dict__) - - if FILTER_DIR: - from_type = [e for e in from_type if not e.startswith('_')] - from_dict = [e for e in from_dict if not e.startswith('_') or - _is_magic(e)] - return sorted(set(extras + from_type + from_dict + - list(self._mock_children))) - - - def __setattr__(self, name, value): - if name in _allowed_names: - # property setters go through here - return object.__setattr__(self, name, value) - elif (self._spec_set and self._mock_methods is not None and - name not in self._mock_methods and - name not in self.__dict__): - raise AttributeError("Mock object has no attribute '%s'" % name) - elif name in _unsupported_magics: - msg = 'Attempting to set unsupported magic method %r.' % name - raise AttributeError(msg) - elif name in _all_magics: - if self._mock_methods is not None and name not in self._mock_methods: - raise AttributeError("Mock object has no attribute '%s'" % name) - - if not _is_instance_mock(value): - setattr(type(self), name, _get_method(name, value)) - original = value - value = lambda *args, **kw: original(self, *args, **kw) - else: - # only set _new_name and not name so that mock_calls is tracked - # but not method calls - _check_and_set_parent(self, value, None, name) - setattr(type(self), name, value) - self._mock_children[name] = value - elif name == '__class__': - self._spec_class = value - return - else: - if _check_and_set_parent(self, value, name, name): - self._mock_children[name] = value - return object.__setattr__(self, name, value) - - - def __delattr__(self, name): - if name in _all_magics and name in type(self).__dict__: - delattr(type(self), name) - if name not in self.__dict__: - # for magic methods that are still MagicProxy objects and - # not set on the instance itself - return - - if name in self.__dict__: - object.__delattr__(self, name) - - obj = self._mock_children.get(name, _missing) - if obj is _deleted: - raise AttributeError(name) - if obj is not _missing: - del self._mock_children[name] - self._mock_children[name] = _deleted - - - - def _format_mock_call_signature(self, args, kwargs): - name = self._mock_name or 'mock' - return _format_call_signature(name, args, kwargs) - - - def _format_mock_failure_message(self, args, kwargs): - message = 'Expected call: %s\nActual call: %s' - expected_string = self._format_mock_call_signature(args, kwargs) - call_args = self.call_args - if len(call_args) == 3: - call_args = call_args[1:] - actual_string = self._format_mock_call_signature(*call_args) - return message % (expected_string, actual_string) - - - def assert_called_with(_mock_self, *args, **kwargs): - """assert that the mock was called with the specified arguments. - - Raises an AssertionError if the args and keyword args passed in are - different to the last call to the mock.""" - self = _mock_self - if self.call_args is None: - expected = self._format_mock_call_signature(args, kwargs) - raise AssertionError('Expected call: %s\nNot called' % (expected,)) - - if self.call_args != (args, kwargs): - msg = self._format_mock_failure_message(args, kwargs) - raise AssertionError(msg) - - - def assert_called_once_with(_mock_self, *args, **kwargs): - """assert that the mock was called exactly once and with the specified - arguments.""" - self = _mock_self - if not self.call_count == 1: - msg = ("Expected to be called once. Called %s times." % - self.call_count) - raise AssertionError(msg) - return self.assert_called_with(*args, **kwargs) - - - def assert_has_calls(self, calls, any_order=False): - """assert the mock has been called with the specified calls. - The `mock_calls` list is checked for the calls. - - If `any_order` is False (the default) then the calls must be - sequential. There can be extra calls before or after the - specified calls. - - If `any_order` is True then the calls can be in any order, but - they must all appear in `mock_calls`.""" - if not any_order: - if calls not in self.mock_calls: - raise AssertionError( - 'Calls not found.\nExpected: %r\n' - 'Actual: %r' % (calls, self.mock_calls) - ) - return - - all_calls = list(self.mock_calls) - - not_found = [] - for kall in calls: - try: - all_calls.remove(kall) - except ValueError: - not_found.append(kall) - if not_found: - raise AssertionError( - '%r not all found in call list' % (tuple(not_found),) - ) - - - def assert_any_call(self, *args, **kwargs): - """assert the mock has been called with the specified arguments. - - The assert passes if the mock has *ever* been called, unlike - `assert_called_with` and `assert_called_once_with` that only pass if - the call is the most recent one.""" - kall = call(*args, **kwargs) - if kall not in self.call_args_list: - expected_string = self._format_mock_call_signature(args, kwargs) - raise AssertionError( - '%s call not found' % expected_string - ) - - - def _get_child_mock(self, **kw): - """Create the child mocks for attributes and return value. - By default child mocks will be the same type as the parent. - Subclasses of Mock may want to override this to customize the way - child mocks are made. - - For non-callable mocks the callable variant will be used (rather than - any custom subclass).""" - _type = type(self) - if not issubclass(_type, CallableMixin): - if issubclass(_type, NonCallableMagicMock): - klass = MagicMock - elif issubclass(_type, NonCallableMock) : - klass = Mock - else: - klass = _type.__mro__[1] - return klass(**kw) - - - -def _try_iter(obj): - if obj is None: - return obj - if _is_exception(obj): - return obj - if _callable(obj): - return obj - try: - return iter(obj) - except TypeError: - # XXXX backwards compatibility - # but this will blow up on first call - so maybe we should fail early? - return obj - - - -class CallableMixin(Base): - - def __init__(self, spec=None, side_effect=None, return_value=DEFAULT, - wraps=None, name=None, spec_set=None, parent=None, - _spec_state=None, _new_name='', _new_parent=None, **kwargs): - self.__dict__['_mock_return_value'] = return_value - - _super(CallableMixin, self).__init__( - spec, wraps, name, spec_set, parent, - _spec_state, _new_name, _new_parent, **kwargs - ) - - self.side_effect = side_effect - - - def _mock_check_sig(self, *args, **kwargs): - # stub method that can be replaced with one with a specific signature - pass - - - def __call__(_mock_self, *args, **kwargs): - # can't use self in-case a function / method we are mocking uses self - # in the signature - _mock_self._mock_check_sig(*args, **kwargs) - return _mock_self._mock_call(*args, **kwargs) - - - def _mock_call(_mock_self, *args, **kwargs): - self = _mock_self - self.called = True - self.call_count += 1 - self.call_args = _Call((args, kwargs), two=True) - self.call_args_list.append(_Call((args, kwargs), two=True)) - - _new_name = self._mock_new_name - _new_parent = self._mock_new_parent - self.mock_calls.append(_Call(('', args, kwargs))) - - seen = set() - skip_next_dot = _new_name == '()' - do_method_calls = self._mock_parent is not None - name = self._mock_name - while _new_parent is not None: - this_mock_call = _Call((_new_name, args, kwargs)) - if _new_parent._mock_new_name: - dot = '.' - if skip_next_dot: - dot = '' - - skip_next_dot = False - if _new_parent._mock_new_name == '()': - skip_next_dot = True - - _new_name = _new_parent._mock_new_name + dot + _new_name - - if do_method_calls: - if _new_name == name: - this_method_call = this_mock_call - else: - this_method_call = _Call((name, args, kwargs)) - _new_parent.method_calls.append(this_method_call) - - do_method_calls = _new_parent._mock_parent is not None - if do_method_calls: - name = _new_parent._mock_name + '.' + name - - _new_parent.mock_calls.append(this_mock_call) - _new_parent = _new_parent._mock_new_parent - - # use ids here so as not to call __hash__ on the mocks - _new_parent_id = id(_new_parent) - if _new_parent_id in seen: - break - seen.add(_new_parent_id) - - ret_val = DEFAULT - effect = self.side_effect - if effect is not None: - if _is_exception(effect): - raise effect - - if not _callable(effect): - result = next(effect) - if _is_exception(result): - raise result - return result - - ret_val = effect(*args, **kwargs) - if ret_val is DEFAULT: - ret_val = self.return_value - - if (self._mock_wraps is not None and - self._mock_return_value is DEFAULT): - return self._mock_wraps(*args, **kwargs) - if ret_val is DEFAULT: - ret_val = self.return_value - return ret_val - - - -class Mock(CallableMixin, NonCallableMock): - """ - Create a new `Mock` object. `Mock` takes several optional arguments - that specify the behaviour of the Mock object: - - * `spec`: This can be either a list of strings or an existing object (a - class or instance) that acts as the specification for the mock object. If - you pass in an object then a list of strings is formed by calling dir on - the object (excluding unsupported magic attributes and methods). Accessing - any attribute not in this list will raise an `AttributeError`. - - If `spec` is an object (rather than a list of strings) then - `mock.__class__` returns the class of the spec object. This allows mocks - to pass `isinstance` tests. - - * `spec_set`: A stricter variant of `spec`. If used, attempting to *set* - or get an attribute on the mock that isn't on the object passed as - `spec_set` will raise an `AttributeError`. - - * `side_effect`: A function to be called whenever the Mock is called. See - the `side_effect` attribute. Useful for raising exceptions or - dynamically changing return values. The function is called with the same - arguments as the mock, and unless it returns `DEFAULT`, the return - value of this function is used as the return value. - - Alternatively `side_effect` can be an exception class or instance. In - this case the exception will be raised when the mock is called. - - If `side_effect` is an iterable then each call to the mock will return - the next value from the iterable. If any of the members of the iterable - are exceptions they will be raised instead of returned. - - * `return_value`: The value returned when the mock is called. By default - this is a new Mock (created on first access). See the - `return_value` attribute. - - * `wraps`: Item for the mock object to wrap. If `wraps` is not None then - calling the Mock will pass the call through to the wrapped object - (returning the real result). Attribute access on the mock will return a - Mock object that wraps the corresponding attribute of the wrapped object - (so attempting to access an attribute that doesn't exist will raise an - `AttributeError`). - - If the mock has an explicit `return_value` set then calls are not passed - to the wrapped object and the `return_value` is returned instead. - - * `name`: If the mock has a name then it will be used in the repr of the - mock. This can be useful for debugging. The name is propagated to child - mocks. - - Mocks can also be called with arbitrary keyword arguments. These will be - used to set attributes on the mock after it is created. - """ - - - -def _dot_lookup(thing, comp, import_path): - try: - return getattr(thing, comp) - except AttributeError: - __import__(import_path) - return getattr(thing, comp) - - -def _importer(target): - components = target.split('.') - import_path = components.pop(0) - thing = __import__(import_path) - - for comp in components: - import_path += ".%s" % comp - thing = _dot_lookup(thing, comp, import_path) - return thing - - -def _is_started(patcher): - # XXXX horrible - return hasattr(patcher, 'is_local') - - -class _patch(object): - - attribute_name = None - _active_patches = set() - - def __init__( - self, getter, attribute, new, spec, create, - spec_set, autospec, new_callable, kwargs - ): - if new_callable is not None: - if new is not DEFAULT: - raise ValueError( - "Cannot use 'new' and 'new_callable' together" - ) - if autospec is not None: - raise ValueError( - "Cannot use 'autospec' and 'new_callable' together" - ) - - self.getter = getter - self.attribute = attribute - self.new = new - self.new_callable = new_callable - self.spec = spec - self.create = create - self.has_local = False - self.spec_set = spec_set - self.autospec = autospec - self.kwargs = kwargs - self.additional_patchers = [] - - - def copy(self): - patcher = _patch( - self.getter, self.attribute, self.new, self.spec, - self.create, self.spec_set, - self.autospec, self.new_callable, self.kwargs - ) - patcher.attribute_name = self.attribute_name - patcher.additional_patchers = [ - p.copy() for p in self.additional_patchers - ] - return patcher - - - def __call__(self, func): - if isinstance(func, ClassTypes): - return self.decorate_class(func) - return self.decorate_callable(func) - - - def decorate_class(self, klass): - for attr in dir(klass): - if not attr.startswith(patch.TEST_PREFIX): - continue - - attr_value = getattr(klass, attr) - if not hasattr(attr_value, "__call__"): - continue - - patcher = self.copy() - setattr(klass, attr, patcher(attr_value)) - return klass - - - def decorate_callable(self, func): - if hasattr(func, 'patchings'): - func.patchings.append(self) - return func - - @wraps(func) - def patched(*args, **keywargs): - # don't use a with here (backwards compatability with Python 2.4) - extra_args = [] - entered_patchers = [] - - # can't use try...except...finally because of Python 2.4 - # compatibility - exc_info = tuple() - try: - try: - for patching in patched.patchings: - arg = patching.__enter__() - entered_patchers.append(patching) - if patching.attribute_name is not None: - keywargs.update(arg) - elif patching.new is DEFAULT: - extra_args.append(arg) - - args += tuple(extra_args) - return func(*args, **keywargs) - except: - if (patching not in entered_patchers and - _is_started(patching)): - # the patcher may have been started, but an exception - # raised whilst entering one of its additional_patchers - entered_patchers.append(patching) - # Pass the exception to __exit__ - exc_info = sys.exc_info() - # re-raise the exception - raise - finally: - for patching in reversed(entered_patchers): - patching.__exit__(*exc_info) - - patched.patchings = [self] - if hasattr(func, 'func_code'): - # not in Python 3 - patched.compat_co_firstlineno = getattr( - func, "compat_co_firstlineno", - func.func_code.co_firstlineno - ) - return patched - - - def get_original(self): - target = self.getter() - name = self.attribute - - original = DEFAULT - local = False - - try: - original = target.__dict__[name] - except (AttributeError, KeyError): - original = getattr(target, name, DEFAULT) - else: - local = True - - if not self.create and original is DEFAULT: - raise AttributeError( - "%s does not have the attribute %r" % (target, name) - ) - return original, local - - - def __enter__(self): - """Perform the patch.""" - new, spec, spec_set = self.new, self.spec, self.spec_set - autospec, kwargs = self.autospec, self.kwargs - new_callable = self.new_callable - self.target = self.getter() - - # normalise False to None - if spec is False: - spec = None - if spec_set is False: - spec_set = None - if autospec is False: - autospec = None - - if spec is not None and autospec is not None: - raise TypeError("Can't specify spec and autospec") - if ((spec is not None or autospec is not None) and - spec_set not in (True, None)): - raise TypeError("Can't provide explicit spec_set *and* spec or autospec") - - original, local = self.get_original() - - if new is DEFAULT and autospec is None: - inherit = False - if spec is True: - # set spec to the object we are replacing - spec = original - if spec_set is True: - spec_set = original - spec = None - elif spec is not None: - if spec_set is True: - spec_set = spec - spec = None - elif spec_set is True: - spec_set = original - - if spec is not None or spec_set is not None: - if original is DEFAULT: - raise TypeError("Can't use 'spec' with create=True") - if isinstance(original, ClassTypes): - # If we're patching out a class and there is a spec - inherit = True - - Klass = MagicMock - _kwargs = {} - if new_callable is not None: - Klass = new_callable - elif spec is not None or spec_set is not None: - this_spec = spec - if spec_set is not None: - this_spec = spec_set - if _is_list(this_spec): - not_callable = '__call__' not in this_spec - else: - not_callable = not _callable(this_spec) - if not_callable: - Klass = NonCallableMagicMock - - if spec is not None: - _kwargs['spec'] = spec - if spec_set is not None: - _kwargs['spec_set'] = spec_set - - # add a name to mocks - if (isinstance(Klass, type) and - issubclass(Klass, NonCallableMock) and self.attribute): - _kwargs['name'] = self.attribute - - _kwargs.update(kwargs) - new = Klass(**_kwargs) - - if inherit and _is_instance_mock(new): - # we can only tell if the instance should be callable if the - # spec is not a list - this_spec = spec - if spec_set is not None: - this_spec = spec_set - if (not _is_list(this_spec) and not - _instance_callable(this_spec)): - Klass = NonCallableMagicMock - - _kwargs.pop('name') - new.return_value = Klass(_new_parent=new, _new_name='()', - **_kwargs) - elif autospec is not None: - # spec is ignored, new *must* be default, spec_set is treated - # as a boolean. Should we check spec is not None and that spec_set - # is a bool? - if new is not DEFAULT: - raise TypeError( - "autospec creates the mock for you. Can't specify " - "autospec and new." - ) - if original is DEFAULT: - raise TypeError("Can't use 'autospec' with create=True") - spec_set = bool(spec_set) - if autospec is True: - autospec = original - - new = create_autospec(autospec, spec_set=spec_set, - _name=self.attribute, **kwargs) - elif kwargs: - # can't set keyword args when we aren't creating the mock - # XXXX If new is a Mock we could call new.configure_mock(**kwargs) - raise TypeError("Can't pass kwargs to a mock we aren't creating") - - new_attr = new - - self.temp_original = original - self.is_local = local - setattr(self.target, self.attribute, new_attr) - if self.attribute_name is not None: - extra_args = {} - if self.new is DEFAULT: - extra_args[self.attribute_name] = new - for patching in self.additional_patchers: - arg = patching.__enter__() - if patching.new is DEFAULT: - extra_args.update(arg) - return extra_args - - return new - - - def __exit__(self, *exc_info): - """Undo the patch.""" - if not _is_started(self): - raise RuntimeError('stop called on unstarted patcher') - - if self.is_local and self.temp_original is not DEFAULT: - setattr(self.target, self.attribute, self.temp_original) - else: - delattr(self.target, self.attribute) - if not self.create and not hasattr(self.target, self.attribute): - # needed for proxy objects like django settings - setattr(self.target, self.attribute, self.temp_original) - - del self.temp_original - del self.is_local - del self.target - for patcher in reversed(self.additional_patchers): - if _is_started(patcher): - patcher.__exit__(*exc_info) - - - def start(self): - """Activate a patch, returning any created mock.""" - result = self.__enter__() - self._active_patches.add(self) - return result - - - def stop(self): - """Stop an active patch.""" - self._active_patches.discard(self) - return self.__exit__() - - - -def _get_target(target): - try: - target, attribute = target.rsplit('.', 1) - except (TypeError, ValueError): - raise TypeError("Need a valid target to patch. You supplied: %r" % - (target,)) - getter = lambda: _importer(target) - return getter, attribute - - -def _patch_object( - target, attribute, new=DEFAULT, spec=None, - create=False, spec_set=None, autospec=None, - new_callable=None, **kwargs - ): - """ - patch.object(target, attribute, new=DEFAULT, spec=None, create=False, - spec_set=None, autospec=None, new_callable=None, **kwargs) - - patch the named member (`attribute`) on an object (`target`) with a mock - object. - - `patch.object` can be used as a decorator, class decorator or a context - manager. Arguments `new`, `spec`, `create`, `spec_set`, - `autospec` and `new_callable` have the same meaning as for `patch`. Like - `patch`, `patch.object` takes arbitrary keyword arguments for configuring - the mock object it creates. - - When used as a class decorator `patch.object` honours `patch.TEST_PREFIX` - for choosing which methods to wrap. - """ - getter = lambda: target - return _patch( - getter, attribute, new, spec, create, - spec_set, autospec, new_callable, kwargs - ) - - -def _patch_multiple(target, spec=None, create=False, spec_set=None, - autospec=None, new_callable=None, **kwargs): - """Perform multiple patches in a single call. It takes the object to be - patched (either as an object or a string to fetch the object by importing) - and keyword arguments for the patches:: - - with patch.multiple(settings, FIRST_PATCH='one', SECOND_PATCH='two'): - ... - - Use `DEFAULT` as the value if you want `patch.multiple` to create - mocks for you. In this case the created mocks are passed into a decorated - function by keyword, and a dictionary is returned when `patch.multiple` is - used as a context manager. - - `patch.multiple` can be used as a decorator, class decorator or a context - manager. The arguments `spec`, `spec_set`, `create`, - `autospec` and `new_callable` have the same meaning as for `patch`. These - arguments will be applied to *all* patches done by `patch.multiple`. - - When used as a class decorator `patch.multiple` honours `patch.TEST_PREFIX` - for choosing which methods to wrap. - """ - if type(target) in (unicode, str): - getter = lambda: _importer(target) - else: - getter = lambda: target - - if not kwargs: - raise ValueError( - 'Must supply at least one keyword argument with patch.multiple' - ) - # need to wrap in a list for python 3, where items is a view - items = list(kwargs.items()) - attribute, new = items[0] - patcher = _patch( - getter, attribute, new, spec, create, spec_set, - autospec, new_callable, {} - ) - patcher.attribute_name = attribute - for attribute, new in items[1:]: - this_patcher = _patch( - getter, attribute, new, spec, create, spec_set, - autospec, new_callable, {} - ) - this_patcher.attribute_name = attribute - patcher.additional_patchers.append(this_patcher) - return patcher - - -def patch( - target, new=DEFAULT, spec=None, create=False, - spec_set=None, autospec=None, new_callable=None, **kwargs - ): - """ - `patch` acts as a function decorator, class decorator or a context - manager. Inside the body of the function or with statement, the `target` - is patched with a `new` object. When the function/with statement exits - the patch is undone. - - If `new` is omitted, then the target is replaced with a - `MagicMock`. If `patch` is used as a decorator and `new` is - omitted, the created mock is passed in as an extra argument to the - decorated function. If `patch` is used as a context manager the created - mock is returned by the context manager. - - `target` should be a string in the form `'package.module.ClassName'`. The - `target` is imported and the specified object replaced with the `new` - object, so the `target` must be importable from the environment you are - calling `patch` from. The target is imported when the decorated function - is executed, not at decoration time. - - The `spec` and `spec_set` keyword arguments are passed to the `MagicMock` - if patch is creating one for you. - - In addition you can pass `spec=True` or `spec_set=True`, which causes - patch to pass in the object being mocked as the spec/spec_set object. - - `new_callable` allows you to specify a different class, or callable object, - that will be called to create the `new` object. By default `MagicMock` is - used. - - A more powerful form of `spec` is `autospec`. If you set `autospec=True` - then the mock with be created with a spec from the object being replaced. - All attributes of the mock will also have the spec of the corresponding - attribute of the object being replaced. Methods and functions being - mocked will have their arguments checked and will raise a `TypeError` if - they are called with the wrong signature. For mocks replacing a class, - their return value (the 'instance') will have the same spec as the class. - - Instead of `autospec=True` you can pass `autospec=some_object` to use an - arbitrary object as the spec instead of the one being replaced. - - By default `patch` will fail to replace attributes that don't exist. If - you pass in `create=True`, and the attribute doesn't exist, patch will - create the attribute for you when the patched function is called, and - delete it again afterwards. This is useful for writing tests against - attributes that your production code creates at runtime. It is off by by - default because it can be dangerous. With it switched on you can write - passing tests against APIs that don't actually exist! - - Patch can be used as a `TestCase` class decorator. It works by - decorating each test method in the class. This reduces the boilerplate - code when your test methods share a common patchings set. `patch` finds - tests by looking for method names that start with `patch.TEST_PREFIX`. - By default this is `test`, which matches the way `unittest` finds tests. - You can specify an alternative prefix by setting `patch.TEST_PREFIX`. - - Patch can be used as a context manager, with the with statement. Here the - patching applies to the indented block after the with statement. If you - use "as" then the patched object will be bound to the name after the - "as"; very useful if `patch` is creating a mock object for you. - - `patch` takes arbitrary keyword arguments. These will be passed to - the `Mock` (or `new_callable`) on construction. - - `patch.dict(...)`, `patch.multiple(...)` and `patch.object(...)` are - available for alternate use-cases. - """ - getter, attribute = _get_target(target) - return _patch( - getter, attribute, new, spec, create, - spec_set, autospec, new_callable, kwargs - ) - - -class _patch_dict(object): - """ - Patch a dictionary, or dictionary like object, and restore the dictionary - to its original state after the test. - - `in_dict` can be a dictionary or a mapping like container. If it is a - mapping then it must at least support getting, setting and deleting items - plus iterating over keys. - - `in_dict` can also be a string specifying the name of the dictionary, which - will then be fetched by importing it. - - `values` can be a dictionary of values to set in the dictionary. `values` - can also be an iterable of `(key, value)` pairs. - - If `clear` is True then the dictionary will be cleared before the new - values are set. - - `patch.dict` can also be called with arbitrary keyword arguments to set - values in the dictionary:: - - with patch.dict('sys.modules', mymodule=Mock(), other_module=Mock()): - ... - - `patch.dict` can be used as a context manager, decorator or class - decorator. When used as a class decorator `patch.dict` honours - `patch.TEST_PREFIX` for choosing which methods to wrap. - """ - - def __init__(self, in_dict, values=(), clear=False, **kwargs): - if isinstance(in_dict, basestring): - in_dict = _importer(in_dict) - self.in_dict = in_dict - # support any argument supported by dict(...) constructor - self.values = dict(values) - self.values.update(kwargs) - self.clear = clear - self._original = None - - - def __call__(self, f): - if isinstance(f, ClassTypes): - return self.decorate_class(f) - @wraps(f) - def _inner(*args, **kw): - self._patch_dict() - try: - return f(*args, **kw) - finally: - self._unpatch_dict() - - return _inner - - - def decorate_class(self, klass): - for attr in dir(klass): - attr_value = getattr(klass, attr) - if (attr.startswith(patch.TEST_PREFIX) and - hasattr(attr_value, "__call__")): - decorator = _patch_dict(self.in_dict, self.values, self.clear) - decorated = decorator(attr_value) - setattr(klass, attr, decorated) - return klass - - - def __enter__(self): - """Patch the dict.""" - self._patch_dict() - - - def _patch_dict(self): - values = self.values - in_dict = self.in_dict - clear = self.clear - - try: - original = in_dict.copy() - except AttributeError: - # dict like object with no copy method - # must support iteration over keys - original = {} - for key in in_dict: - original[key] = in_dict[key] - self._original = original - - if clear: - _clear_dict(in_dict) - - try: - in_dict.update(values) - except AttributeError: - # dict like object with no update method - for key in values: - in_dict[key] = values[key] - - - def _unpatch_dict(self): - in_dict = self.in_dict - original = self._original - - _clear_dict(in_dict) - - try: - in_dict.update(original) - except AttributeError: - for key in original: - in_dict[key] = original[key] - - - def __exit__(self, *args): - """Unpatch the dict.""" - self._unpatch_dict() - return False - - start = __enter__ - stop = __exit__ - - -def _clear_dict(in_dict): - try: - in_dict.clear() - except AttributeError: - keys = list(in_dict) - for key in keys: - del in_dict[key] - - -def _patch_stopall(): - """Stop all active patches.""" - for patch in list(_patch._active_patches): - patch.stop() - - -patch.object = _patch_object -patch.dict = _patch_dict -patch.multiple = _patch_multiple -patch.stopall = _patch_stopall -patch.TEST_PREFIX = 'test' - -magic_methods = ( - "lt le gt ge eq ne " - "getitem setitem delitem " - "len contains iter " - "hash str sizeof " - "enter exit " - "divmod neg pos abs invert " - "complex int float index " - "trunc floor ceil " -) - -numerics = "add sub mul div floordiv mod lshift rshift and xor or pow " -inplace = ' '.join('i%s' % n for n in numerics.split()) -right = ' '.join('r%s' % n for n in numerics.split()) -extra = '' -if inPy3k: - extra = 'bool next ' -else: - extra = 'unicode long nonzero oct hex truediv rtruediv ' - -# not including __prepare__, __instancecheck__, __subclasscheck__ -# (as they are metaclass methods) -# __del__ is not supported at all as it causes problems if it exists - -_non_defaults = set('__%s__' % method for method in [ - 'cmp', 'getslice', 'setslice', 'coerce', 'subclasses', - 'format', 'get', 'set', 'delete', 'reversed', - 'missing', 'reduce', 'reduce_ex', 'getinitargs', - 'getnewargs', 'getstate', 'setstate', 'getformat', - 'setformat', 'repr', 'dir' -]) - - -def _get_method(name, func): - "Turns a callable object (like a mock) into a real function" - def method(self, *args, **kw): - return func(self, *args, **kw) - method.__name__ = name - return method - - -_magics = set( - '__%s__' % method for method in - ' '.join([magic_methods, numerics, inplace, right, extra]).split() -) - -_all_magics = _magics | _non_defaults - -_unsupported_magics = set([ - '__getattr__', '__setattr__', - '__init__', '__new__', '__prepare__' - '__instancecheck__', '__subclasscheck__', - '__del__' -]) - -_calculate_return_value = { - '__hash__': lambda self: object.__hash__(self), - '__str__': lambda self: object.__str__(self), - '__sizeof__': lambda self: object.__sizeof__(self), - '__unicode__': lambda self: unicode(object.__str__(self)), -} - -_return_values = { - '__lt__': NotImplemented, - '__gt__': NotImplemented, - '__le__': NotImplemented, - '__ge__': NotImplemented, - '__int__': 1, - '__contains__': False, - '__len__': 0, - '__exit__': False, - '__complex__': 1j, - '__float__': 1.0, - '__bool__': True, - '__nonzero__': True, - '__oct__': '1', - '__hex__': '0x1', - '__long__': long(1), - '__index__': 1, -} - - -def _get_eq(self): - def __eq__(other): - ret_val = self.__eq__._mock_return_value - if ret_val is not DEFAULT: - return ret_val - return self is other - return __eq__ - -def _get_ne(self): - def __ne__(other): - if self.__ne__._mock_return_value is not DEFAULT: - return DEFAULT - return self is not other - return __ne__ - -def _get_iter(self): - def __iter__(): - ret_val = self.__iter__._mock_return_value - if ret_val is DEFAULT: - return iter([]) - # if ret_val was already an iterator, then calling iter on it should - # return the iterator unchanged - return iter(ret_val) - return __iter__ - -_side_effect_methods = { - '__eq__': _get_eq, - '__ne__': _get_ne, - '__iter__': _get_iter, -} - - - -def _set_return_value(mock, method, name): - fixed = _return_values.get(name, DEFAULT) - if fixed is not DEFAULT: - method.return_value = fixed - return - - return_calulator = _calculate_return_value.get(name) - if return_calulator is not None: - try: - return_value = return_calulator(mock) - except AttributeError: - # XXXX why do we return AttributeError here? - # set it as a side_effect instead? - return_value = AttributeError(name) - method.return_value = return_value - return - - side_effector = _side_effect_methods.get(name) - if side_effector is not None: - method.side_effect = side_effector(mock) - - - -class MagicMixin(object): - def __init__(self, *args, **kw): - _super(MagicMixin, self).__init__(*args, **kw) - self._mock_set_magics() - - - def _mock_set_magics(self): - these_magics = _magics - - if self._mock_methods is not None: - these_magics = _magics.intersection(self._mock_methods) - - remove_magics = set() - remove_magics = _magics - these_magics - - for entry in remove_magics: - if entry in type(self).__dict__: - # remove unneeded magic methods - delattr(self, entry) - - # don't overwrite existing attributes if called a second time - these_magics = these_magics - set(type(self).__dict__) - - _type = type(self) - for entry in these_magics: - setattr(_type, entry, MagicProxy(entry, self)) - - - -class NonCallableMagicMock(MagicMixin, NonCallableMock): - """A version of `MagicMock` that isn't callable.""" - def mock_add_spec(self, spec, spec_set=False): - """Add a spec to a mock. `spec` can either be an object or a - list of strings. Only attributes on the `spec` can be fetched as - attributes from the mock. - - If `spec_set` is True then only attributes on the spec can be set.""" - self._mock_add_spec(spec, spec_set) - self._mock_set_magics() - - - -class MagicMock(MagicMixin, Mock): - """ - MagicMock is a subclass of Mock with default implementations - of most of the magic methods. You can use MagicMock without having to - configure the magic methods yourself. - - If you use the `spec` or `spec_set` arguments then *only* magic - methods that exist in the spec will be created. - - Attributes and the return value of a `MagicMock` will also be `MagicMocks`. - """ - def mock_add_spec(self, spec, spec_set=False): - """Add a spec to a mock. `spec` can either be an object or a - list of strings. Only attributes on the `spec` can be fetched as - attributes from the mock. - - If `spec_set` is True then only attributes on the spec can be set.""" - self._mock_add_spec(spec, spec_set) - self._mock_set_magics() - - - -class MagicProxy(object): - def __init__(self, name, parent): - self.name = name - self.parent = parent - - def __call__(self, *args, **kwargs): - m = self.create_mock() - return m(*args, **kwargs) - - def create_mock(self): - entry = self.name - parent = self.parent - m = parent._get_child_mock(name=entry, _new_name=entry, - _new_parent=parent) - setattr(parent, entry, m) - _set_return_value(parent, m, entry) - return m - - def __get__(self, obj, _type=None): - return self.create_mock() - - - -class _ANY(object): - "A helper object that compares equal to everything." - - def __eq__(self, other): - return True - - def __ne__(self, other): - return False - - def __repr__(self): - return '' - -ANY = _ANY() - - - -def _format_call_signature(name, args, kwargs): - message = '%s(%%s)' % name - formatted_args = '' - args_string = ', '.join([repr(arg) for arg in args]) - kwargs_string = ', '.join([ - '%s=%r' % (key, value) for key, value in kwargs.items() - ]) - if args_string: - formatted_args = args_string - if kwargs_string: - if formatted_args: - formatted_args += ', ' - formatted_args += kwargs_string - - return message % formatted_args - - - -class _Call(tuple): - """ - A tuple for holding the results of a call to a mock, either in the form - `(args, kwargs)` or `(name, args, kwargs)`. - - If args or kwargs are empty then a call tuple will compare equal to - a tuple without those values. This makes comparisons less verbose:: - - _Call(('name', (), {})) == ('name',) - _Call(('name', (1,), {})) == ('name', (1,)) - _Call(((), {'a': 'b'})) == ({'a': 'b'},) - - The `_Call` object provides a useful shortcut for comparing with call:: - - _Call(((1, 2), {'a': 3})) == call(1, 2, a=3) - _Call(('foo', (1, 2), {'a': 3})) == call.foo(1, 2, a=3) - - If the _Call has no name then it will match any name. - """ - def __new__(cls, value=(), name=None, parent=None, two=False, - from_kall=True): - name = '' - args = () - kwargs = {} - _len = len(value) - if _len == 3: - name, args, kwargs = value - elif _len == 2: - first, second = value - if isinstance(first, basestring): - name = first - if isinstance(second, tuple): - args = second - else: - kwargs = second - else: - args, kwargs = first, second - elif _len == 1: - value, = value - if isinstance(value, basestring): - name = value - elif isinstance(value, tuple): - args = value - else: - kwargs = value - - if two: - return tuple.__new__(cls, (args, kwargs)) - - return tuple.__new__(cls, (name, args, kwargs)) - - - def __init__(self, value=(), name=None, parent=None, two=False, - from_kall=True): - self.name = name - self.parent = parent - self.from_kall = from_kall - - - def __eq__(self, other): - if other is ANY: - return True - try: - len_other = len(other) - except TypeError: - return False - - self_name = '' - if len(self) == 2: - self_args, self_kwargs = self - else: - self_name, self_args, self_kwargs = self - - other_name = '' - if len_other == 0: - other_args, other_kwargs = (), {} - elif len_other == 3: - other_name, other_args, other_kwargs = other - elif len_other == 1: - value, = other - if isinstance(value, tuple): - other_args = value - other_kwargs = {} - elif isinstance(value, basestring): - other_name = value - other_args, other_kwargs = (), {} - else: - other_args = () - other_kwargs = value - else: - # len 2 - # could be (name, args) or (name, kwargs) or (args, kwargs) - first, second = other - if isinstance(first, basestring): - other_name = first - if isinstance(second, tuple): - other_args, other_kwargs = second, {} - else: - other_args, other_kwargs = (), second - else: - other_args, other_kwargs = first, second - - if self_name and other_name != self_name: - return False - - # this order is important for ANY to work! - return (other_args, other_kwargs) == (self_args, self_kwargs) - - - def __ne__(self, other): - return not self.__eq__(other) - - - def __call__(self, *args, **kwargs): - if self.name is None: - return _Call(('', args, kwargs), name='()') - - name = self.name + '()' - return _Call((self.name, args, kwargs), name=name, parent=self) - - - def __getattr__(self, attr): - if self.name is None: - return _Call(name=attr, from_kall=False) - name = '%s.%s' % (self.name, attr) - return _Call(name=name, parent=self, from_kall=False) - - - def __repr__(self): - if not self.from_kall: - name = self.name or 'call' - if name.startswith('()'): - name = 'call%s' % name - return name - - if len(self) == 2: - name = 'call' - args, kwargs = self - else: - name, args, kwargs = self - if not name: - name = 'call' - elif not name.startswith('()'): - name = 'call.%s' % name - else: - name = 'call%s' % name - return _format_call_signature(name, args, kwargs) - - - def call_list(self): - """For a call object that represents multiple calls, `call_list` - returns a list of all the intermediate calls as well as the - final call.""" - vals = [] - thing = self - while thing is not None: - if thing.from_kall: - vals.append(thing) - thing = thing.parent - return _CallList(reversed(vals)) - - -call = _Call(from_kall=False) - - - -def create_autospec(spec, spec_set=False, instance=False, _parent=None, - _name=None, **kwargs): - """Create a mock object using another object as a spec. Attributes on the - mock will use the corresponding attribute on the `spec` object as their - spec. - - Functions or methods being mocked will have their arguments checked - to check that they are called with the correct signature. - - If `spec_set` is True then attempting to set attributes that don't exist - on the spec object will raise an `AttributeError`. - - If a class is used as a spec then the return value of the mock (the - instance of the class) will have the same spec. You can use a class as the - spec for an instance object by passing `instance=True`. The returned mock - will only be callable if instances of the mock are callable. - - `create_autospec` also takes arbitrary keyword arguments that are passed to - the constructor of the created mock.""" - if _is_list(spec): - # can't pass a list instance to the mock constructor as it will be - # interpreted as a list of strings - spec = type(spec) - - is_type = isinstance(spec, ClassTypes) - - _kwargs = {'spec': spec} - if spec_set: - _kwargs = {'spec_set': spec} - elif spec is None: - # None we mock with a normal mock without a spec - _kwargs = {} - - _kwargs.update(kwargs) - - Klass = MagicMock - if type(spec) in DescriptorTypes: - # descriptors don't have a spec - # because we don't know what type they return - _kwargs = {} - elif not _callable(spec): - Klass = NonCallableMagicMock - elif is_type and instance and not _instance_callable(spec): - Klass = NonCallableMagicMock - - _new_name = _name - if _parent is None: - # for a top level object no _new_name should be set - _new_name = '' - - mock = Klass(parent=_parent, _new_parent=_parent, _new_name=_new_name, - name=_name, **_kwargs) - - if isinstance(spec, FunctionTypes): - # should only happen at the top level because we don't - # recurse for functions - mock = _set_signature(mock, spec) - else: - _check_signature(spec, mock, is_type, instance) - - if _parent is not None and not instance: - _parent._mock_children[_name] = mock - - if is_type and not instance and 'return_value' not in kwargs: - mock.return_value = create_autospec(spec, spec_set, instance=True, - _name='()', _parent=mock) - - for entry in dir(spec): - if _is_magic(entry): - # MagicMock already does the useful magic methods for us - continue - - if isinstance(spec, FunctionTypes) and entry in FunctionAttributes: - # allow a mock to actually be a function - continue - - # XXXX do we need a better way of getting attributes without - # triggering code execution (?) Probably not - we need the actual - # object to mock it so we would rather trigger a property than mock - # the property descriptor. Likewise we want to mock out dynamically - # provided attributes. - # XXXX what about attributes that raise exceptions other than - # AttributeError on being fetched? - # we could be resilient against it, or catch and propagate the - # exception when the attribute is fetched from the mock - try: - original = getattr(spec, entry) - except AttributeError: - continue - - kwargs = {'spec': original} - if spec_set: - kwargs = {'spec_set': original} - - if not isinstance(original, FunctionTypes): - new = _SpecState(original, spec_set, mock, entry, instance) - mock._mock_children[entry] = new - else: - parent = mock - if isinstance(spec, FunctionTypes): - parent = mock.mock - - new = MagicMock(parent=parent, name=entry, _new_name=entry, - _new_parent=parent, **kwargs) - mock._mock_children[entry] = new - skipfirst = _must_skip(spec, entry, is_type) - _check_signature(original, new, skipfirst=skipfirst) - - # so functions created with _set_signature become instance attributes, - # *plus* their underlying mock exists in _mock_children of the parent - # mock. Adding to _mock_children may be unnecessary where we are also - # setting as an instance attribute? - if isinstance(new, FunctionTypes): - setattr(mock, entry, new) - - return mock - - -def _must_skip(spec, entry, is_type): - if not isinstance(spec, ClassTypes): - if entry in getattr(spec, '__dict__', {}): - # instance attribute - shouldn't skip - return False - spec = spec.__class__ - if not hasattr(spec, '__mro__'): - # old style class: can't have descriptors anyway - return is_type - - for klass in spec.__mro__: - result = klass.__dict__.get(entry, DEFAULT) - if result is DEFAULT: - continue - if isinstance(result, (staticmethod, classmethod)): - return False - return is_type - - # shouldn't get here unless function is a dynamically provided attribute - # XXXX untested behaviour - return is_type - - -def _get_class(obj): - try: - return obj.__class__ - except AttributeError: - # in Python 2, _sre.SRE_Pattern objects have no __class__ - return type(obj) - - -class _SpecState(object): - - def __init__(self, spec, spec_set=False, parent=None, - name=None, ids=None, instance=False): - self.spec = spec - self.ids = ids - self.spec_set = spec_set - self.parent = parent - self.instance = instance - self.name = name - - -FunctionTypes = ( - # python function - type(create_autospec), - # instance method - type(ANY.__eq__), - # unbound method - type(_ANY.__eq__), -) - -FunctionAttributes = set([ - 'func_closure', - 'func_code', - 'func_defaults', - 'func_dict', - 'func_doc', - 'func_globals', - 'func_name', -]) - - -file_spec = None - - -def mock_open(mock=None, read_data=''): - """ - A helper function to create a mock to replace the use of `open`. It works - for `open` called directly or used as a context manager. - - The `mock` argument is the mock object to configure. If `None` (the - default) then a `MagicMock` will be created for you, with the API limited - to methods or attributes available on standard file handles. - - `read_data` is a string for the `read` method of the file handle to return. - This is an empty string by default. - """ - global file_spec - if file_spec is None: - # set on first use - try: - file_spec = file - except NameError: - import _io - file_spec = list(set(dir(_io.TextIOWrapper)).union(set(dir(_io.BytesIO)))) - - if mock is None: - mock = MagicMock(name='open', spec=open) - - handle = MagicMock(spec=file_spec) - handle.write.return_value = None - handle.__enter__.return_value = handle - handle.read.return_value = read_data - - mock.return_value = handle - return mock - - -class PropertyMock(Mock): - """ - A mock intended to be used as a property, or other descriptor, on a class. - `PropertyMock` provides `__get__` and `__set__` methods so you can specify - a return value when it is fetched. - - Fetching a `PropertyMock` instance from an object calls the mock, with - no args. Setting it calls the mock with the value being set. - """ - def _get_child_mock(self, **kwargs): - return MagicMock(**kwargs) - - def __get__(self, obj, obj_type): - return self() - def __set__(self, obj, val): - self(val) diff -Nru pyglet-1.4.10/tests/__init__.py pyglet-1.5.14/tests/__init__.py --- pyglet-1.4.10/tests/__init__.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/__init__.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,35 +1,4 @@ """ Please see doc/internal/testing.txt for test documentation. """ - -# Try to get a version of mock -try: - # Python 3.3+ has mock in the stdlib - import unittest.mock as mock -except ImportError: - try: - # User could have mock installed - import mock - except ImportError: - # Last resort: use included mock library - import tests.extlibs.mock as mock - -# Try to get python-future -try: - import future -except ImportError: - import os.path as op - import sys - future_base = op.abspath(op.join(op.dirname(__file__), 'extlibs', 'future')) - sys.path.insert(0, op.join(future_base, 'py2_3')) - if sys.version_info[:2] < (3, 0): - sys.path.insert(0, op.join(future_base, 'py2')) - del future_base - del sys - del op - try: - import future - except ImportError: - print('Failed to get python-future') - raise - +import unittest.mock as mock diff -Nru pyglet-1.4.10/tests/integration/graphics/graphics_common.py pyglet-1.5.14/tests/integration/graphics/graphics_common.py --- pyglet-1.4.10/tests/integration/graphics/graphics_common.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/graphics/graphics_common.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,9 +1,3 @@ -#!/usr/bin/env python - -from __future__ import division -from builtins import zip -from builtins import range -from builtins import object from abc import abstractmethod import random from collections import deque @@ -49,7 +43,7 @@ return vertices, colors, tex_coords -class GraphicsGenericTestCase(object): +class GraphicsGenericTestCase: """ A generic test for asserting vertices positions using openGL Feedback Buffer. diff -Nru pyglet-1.4.10/tests/integration/graphics/test_allocation.py pyglet-1.5.14/tests/integration/graphics/test_allocation.py --- pyglet-1.4.10/tests/integration/graphics/test_allocation.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/graphics/test_allocation.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,14 +1,9 @@ -from builtins import zip -from builtins import next -from builtins import range -from builtins import object - import random from pyglet.graphics import allocation -class Region(object): +class Region: def __init__(self, start, size): self.start = start self.size = size @@ -21,7 +16,7 @@ return 'Region(%r, %r)' % (self.start, self.size) -class RegionAllocator(object): +class RegionAllocator: def __init__(self, capacity): self.allocator = allocation.Allocator(capacity) self.regions = [] diff -Nru pyglet-1.4.10/tests/integration/graphics/test_immediate_drawing_indexed_data.py pyglet-1.5.14/tests/integration/graphics/test_immediate_drawing_indexed_data.py --- pyglet-1.4.10/tests/integration/graphics/test_immediate_drawing_indexed_data.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/graphics/test_immediate_drawing_indexed_data.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,7 +1,5 @@ -#!/usr/bin/env python """Tests immediate drawing using indexed data. """ -from __future__ import absolute_import import unittest import pyglet diff -Nru pyglet-1.4.10/tests/integration/graphics/test_immediate_drawing.py pyglet-1.5.14/tests/integration/graphics/test_immediate_drawing.py --- pyglet-1.4.10/tests/integration/graphics/test_immediate_drawing.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/graphics/test_immediate_drawing.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,7 +1,5 @@ -#!/usr/bin/env python """Tests immediate drawing. """ -from __future__ import absolute_import import unittest import pyglet diff -Nru pyglet-1.4.10/tests/integration/graphics/test_multitexture.py pyglet-1.5.14/tests/integration/graphics/test_multitexture.py --- pyglet-1.4.10/tests/integration/graphics/test_multitexture.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/graphics/test_multitexture.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,6 +1,4 @@ -#!/usr/bin/env python - -'''Draws a full-window quad with two texture units enabled and multi +"""Draws a full-window quad with two texture units enabled and multi texcoords. Texture unit 0 is a checker pattern of yellow and cyan with env mode replace. Texture unit 1 is a checker pattern of cyan and yellow, with env mode modulate. The result should be flat green (with some variation @@ -9,14 +7,14 @@ The test will correctly detect the asbence of multitexturing, or if texture coords are not supplied for a unit, but will still pass if the texture coordinates for each unit are swapped (the tex coords are identical). -''' -from builtins import map +""" import unittest import pyglet from pyglet.gl import * + class MultiTextureTestCase(unittest.TestCase): def test_multitexture(self): window = pyglet.window.Window(width=64, height=64) @@ -33,11 +31,11 @@ texture1 = pattern1.create_image(64, 64).get_texture() batch = pyglet.graphics.Batch() - batch.add(4, GL_QUADS, None, - ('v2i', [0, 0, w, 0, w, h, 0, h]), - ('0t3f', texture1.tex_coords), - ('1t3f', texture0.tex_coords) - ) + batch.add(4, GL_QUADS, None, + ('v2i', [0, 0, w, 0, w, h, 0, h]), + ('0t3f', texture1.tex_coords), + ('1t3f', texture0.tex_coords) + ) glActiveTexture(GL_TEXTURE0) glEnable(texture0.target) @@ -54,20 +52,19 @@ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST) batch.draw() - self.assertEqual(self.sample(2, 2), (0, 255, 0)) - self.assertEqual(self.sample(62, 2), (0, 255, 0)) + self.assertEqual(self.sample(2, 2), (0, 255, 0)) + self.assertEqual(self.sample(62, 2), (0, 255, 0)) self.assertEqual(self.sample(62, 62), (0, 255, 0)) - self.assertEqual(self.sample(2, 62), (0, 255, 0)) + self.assertEqual(self.sample(2, 62), (0, 255, 0)) window.close() - + def sample(self, x, y): color_buffer = pyglet.image.get_buffer_manager().get_color_buffer() buffer = color_buffer.get_image_data() data = buffer.get_data('RGB', buffer.pitch) i = y * buffer.pitch + x * 3 - r, g, b = data[i:i+3] + r, g, b = data[i:i + 3] if type(r) is str: r, g, b = list(map(ord, (r, g, b))) return r, g, b - diff -Nru pyglet-1.4.10/tests/integration/graphics/test_retained_drawing_indexed_data.py pyglet-1.5.14/tests/integration/graphics/test_retained_drawing_indexed_data.py --- pyglet-1.4.10/tests/integration/graphics/test_retained_drawing_indexed_data.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/graphics/test_retained_drawing_indexed_data.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,7 +1,6 @@ -#!/usr/bin/env python """Tests vertex list drawing using indexed data. """ -from __future__ import absolute_import + import unittest import pyglet diff -Nru pyglet-1.4.10/tests/integration/graphics/test_retained_drawing.py pyglet-1.5.14/tests/integration/graphics/test_retained_drawing.py --- pyglet-1.4.10/tests/integration/graphics/test_retained_drawing.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/graphics/test_retained_drawing.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,7 +1,5 @@ -#!/usr/bin/env python """Tests vertex list drawing. """ -from __future__ import absolute_import import unittest import pyglet diff -Nru pyglet-1.4.10/tests/integration/image/test_gdkpixbuf2.py pyglet-1.5.14/tests/integration/image/test_gdkpixbuf2.py --- pyglet-1.4.10/tests/integration/image/test_gdkpixbuf2.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/image/test_gdkpixbuf2.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import unittest from ...base.data import PygletTestCase diff -Nru pyglet-1.4.10/tests/integration/image/test_imagegrid.py pyglet-1.5.14/tests/integration/image/test_imagegrid.py --- pyglet-1.4.10/tests/integration/image/test_imagegrid.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/image/test_imagegrid.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,15 +1,16 @@ -from __future__ import absolute_import -from builtins import range import unittest -from pyglet.gl import * from pyglet.image import * from pyglet.window import * -from .texture_compat import colorbyte + +def colorbyte(color): + return bytes((color,)) + class ImageGridTestCase(unittest.TestCase): """Test the ImageGrid for textures.""" + def set_grid_image(self, itemwidth, itemheight, rows, cols, rowpad, colpad): data = b'' color = 1 @@ -29,7 +30,7 @@ assert len(data) == width * height self.image = ImageData(width, height, 'L', data) self.grid = ImageGrid(self.image, rows, cols, - itemwidth, itemheight, rowpad, colpad).get_texture_sequence() + itemwidth, itemheight, rowpad, colpad).get_texture_sequence() def check_cell(self, cellimage, cellindex): self.assertTrue(cellimage.width == self.grid.item_width) @@ -47,21 +48,21 @@ # Test a 3x3 grid with no padding and 4x4 images rows = cols = 3 self.set_grid_image(4, 4, rows, cols, 0, 0) - for i in range(rows * cols): + for i in range(rows * cols): self.check_cell(self.grid[i], i) def testRect(self): # Test a 2x5 grid with no padding and 3x8 images rows, cols = 2, 5 self.set_grid_image(3, 8, rows, cols, 0, 0) - for i in range(rows * cols): + for i in range(rows * cols): self.check_cell(self.grid[i], i) def testPad(self): # Test a 5x3 grid with rowpad=3 and colpad=7 and 10x9 images rows, cols = 5, 3 self.set_grid_image(10, 9, rows, cols, 3, 7) - for i in range(rows * cols): + for i in range(rows * cols): self.check_cell(self.grid[i], i) def testTuple(self): @@ -84,10 +85,9 @@ # Test range over tuples rows, cols = 10, 10 self.set_grid_image(4, 4, rows, cols, 0, 0) - images = self.grid[(3,2):(6,5)] + images = self.grid[(3, 2):(6, 5)] i = 0 - for row in range(3,6): - for col in range(2,5): + for row in range(3, 6): + for col in range(2, 5): self.check_cell(images[i], row * cols + col) i += 1 - diff -Nru pyglet-1.4.10/tests/integration/image/test_texture3d.py pyglet-1.5.14/tests/integration/image/test_texture3d.py --- pyglet-1.4.10/tests/integration/image/test_texture3d.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/image/test_texture3d.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,17 +1,18 @@ -from __future__ import absolute_import -from builtins import range import unittest -from pyglet.gl import * from pyglet.image import * from pyglet.window import * -from .texture_compat import colorbyte + +def colorbyte(color): + return bytes((color,)) + class TestTexture3D(unittest.TestCase): """Test the Texture3D for image grids.""" + def create_image(self, width, height, color): - data = colorbyte(color) * (width * height) + data = colorbyte(color) * (width * height) return ImageData(width, height, 'L', data) def check_image(self, image, width, height, color): @@ -40,7 +41,7 @@ assert len(data) == width * height self.image = ImageData(width, height, 'L', data) grid = ImageGrid(self.image, rows, cols, - itemwidth, itemheight, rowpad, colpad) + itemwidth, itemheight, rowpad, colpad) self.grid = Texture3D.create_for_image_grid(grid) def check_cell(self, cellimage, cellindex): @@ -56,27 +57,27 @@ def test2(self): # Test 2 images of 32x32 - images = [self.create_image(32, 32, i+1) for i in range(2)] + images = [self.create_image(32, 32, i + 1) for i in range(2)] texture = Texture3D.create_for_images(images) self.assertTrue(len(texture) == 2) for i in range(2): - self.check_image(texture[i], 32, 32, i+1) + self.check_image(texture[i], 32, 32, i + 1) def test5(self): # test 5 images of 31x94 (power2 issues) - images = [self.create_image(31, 94, i+1) for i in range(5)] + images = [self.create_image(31, 94, i + 1) for i in range(5)] texture = Texture3D.create_for_images(images) self.assertTrue(len(texture) == 5) for i in range(5): - self.check_image(texture[i], 31, 94, i+1) + self.check_image(texture[i], 31, 94, i + 1) def testSet(self): # test replacing an image - images = [self.create_image(32, 32, i+1) for i in range(3)] + images = [self.create_image(32, 32, i + 1) for i in range(3)] texture = Texture3D.create_for_images(images) self.assertTrue(len(texture) == 3) for i in range(3): - self.check_image(texture[i], 32, 32, i+1) + self.check_image(texture[i], 32, 32, i + 1) texture[1] = self.create_image(32, 32, 87) self.check_image(texture[0], 32, 32, 1) self.check_image(texture[1], 32, 32, 87) @@ -86,20 +87,19 @@ # Test a 3x3 grid with no padding and 4x4 images rows = cols = 3 self.set_grid_image(4, 4, rows, cols, 0, 0) - for i in range(rows * cols): + for i in range(rows * cols): self.check_cell(self.grid[i], i) def testRect(self): # Test a 2x5 grid with no padding and 3x8 images rows, cols = 2, 5 self.set_grid_image(3, 8, rows, cols, 0, 0) - for i in range(rows * cols): + for i in range(rows * cols): self.check_cell(self.grid[i], i) def testPad(self): # Test a 5x3 grid with rowpad=3 and colpad=7 and 10x9 images rows, cols = 5, 3 self.set_grid_image(10, 9, rows, cols, 3, 7) - for i in range(rows * cols): + for i in range(rows * cols): self.check_cell(self.grid[i], i) - diff -Nru pyglet-1.4.10/tests/integration/image/texture_compat.py pyglet-1.5.14/tests/integration/image/texture_compat.py --- pyglet-1.4.10/tests/integration/image/texture_compat.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/image/texture_compat.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -"""Python 2/3 compatibility functions for texture tests.""" -from builtins import bytes -import sys - - -def colorbyte(color): - if sys.version.startswith('2'): - return '%c' % color - else: - return bytes((color,)) - diff -Nru pyglet-1.4.10/tests/integration/media/mock_player.py pyglet-1.5.14/tests/integration/media/mock_player.py --- pyglet-1.4.10/tests/integration/media/mock_player.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/media/mock_player.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,10 +1,10 @@ -from __future__ import absolute_import, print_function import pyglet import pytest + _debug = False -class MockPlayer(object): +class MockPlayer: def __init__(self, event_loop): self.event_loop = event_loop self.events = [] diff -Nru pyglet-1.4.10/tests/integration/media/test_driver.py pyglet-1.5.14/tests/integration/media/test_driver.py --- pyglet-1.4.10/tests/integration/media/test_driver.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/media/test_driver.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,8 +1,6 @@ """Test a specific audio driver for platform. Only checks the use of the interface. Any playback is silent.""" -from __future__ import absolute_import, print_function -from tests import mock import pytest import time diff -Nru pyglet-1.4.10/tests/integration/media/test_openal.py pyglet-1.5.14/tests/integration/media/test_openal.py --- pyglet-1.4.10/tests/integration/media/test_openal.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/media/test_openal.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,6 +1,4 @@ -from builtins import str, isinstance import pytest -from tests import mock import time import weakref diff -Nru pyglet-1.4.10/tests/integration/media/test_player.py pyglet-1.5.14/tests/integration/media/test_player.py --- pyglet-1.4.10/tests/integration/media/test_player.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/media/test_player.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,6 +1,3 @@ -from __future__ import print_function -from future import standard_library -standard_library.install_aliases() import pytest from tests import mock diff -Nru pyglet-1.4.10/tests/integration/media/test_pulse.py pyglet-1.5.14/tests/integration/media/test_pulse.py --- pyglet-1.4.10/tests/integration/media/test_pulse.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/media/test_pulse.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,6 +1,3 @@ -from __future__ import unicode_literals - -from builtins import str import numbers import pytest from threading import Timer diff -Nru pyglet-1.4.10/tests/integration/model/test_loading.py pyglet-1.5.14/tests/integration/model/test_loading.py --- pyglet-1.4.10/tests/integration/model/test_loading.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/model/test_loading.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,13 +1,10 @@ +import io import os -from io import StringIO import pytest import pyglet -from pyglet.compat import BytesIO from pyglet.model import ModelDecodeException -from ...annotations import require_python_version - test_data_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'data')) @@ -26,11 +23,10 @@ assert isinstance(model, pyglet.model.Model) -@require_python_version((3, 4)) def test_load_from_object_str(): file_path = get_test_data_file('models', 'logo3d.obj') with open(file_path, 'r') as f: - file_obj = StringIO(f.read()) + file_obj = io.StringIO(f.read()) model = pyglet.model.load(file_path, file=file_obj) assert isinstance(model, pyglet.model.Model) @@ -38,13 +34,13 @@ def test_load_from_object_bytes(): file_path = get_test_data_file('models', 'logo3d.obj') with open(file_path, 'rb') as f: - file_obj = pyglet.compat.BytesIO(f.read()) + file_obj = io.BytesIO(f.read()) model = pyglet.model.load(file_path, file=file_obj) assert isinstance(model, pyglet.model.Model) def test_no_decoders_available(): - ### This is NOT a valid model file: + # This is NOT a valid model file: file_path = get_test_data_file('media', 'alert.wav') with pytest.raises(ModelDecodeException) as e: diff -Nru pyglet-1.4.10/tests/integration/resource/test_resource_loading.py pyglet-1.5.14/tests/integration/resource/test_resource_loading.py --- pyglet-1.4.10/tests/integration/resource/test_resource_loading.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/tests/integration/resource/test_resource_loading.py 2020-11-10 10:36:26.000000000 +0000 @@ -23,7 +23,7 @@ import pytest from pyglet import resource -from pyglet.compat import asbytes +from pyglet.util import asbytes @pytest.fixture diff -Nru pyglet-1.4.10/tests/integration/window/test_event_sequence.py pyglet-1.5.14/tests/integration/window/test_event_sequence.py --- pyglet-1.4.10/tests/integration/window/test_event_sequence.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/integration/window/test_event_sequence.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,7 +1,3 @@ -from future.standard_library import install_aliases -install_aliases() - -from builtins import object import queue import unittest import time @@ -9,7 +5,7 @@ from pyglet import window -class EventSequenceFixture(object): +class EventSequenceFixture: def __init__(self, event_loop): self.event_loop = event_loop self.listen_events = [] @@ -42,7 +38,7 @@ self.event_loop.interrupt_event_loop() -class EventSequenceTest(object): +class EventSequenceTest: """Base for testing event sequences on a window.""" next_sequence = 0 last_sequence = 0 diff -Nru pyglet-1.4.10/tests/integration/window/test_window_caption.py pyglet-1.5.14/tests/integration/window/test_window_caption.py --- pyglet-1.4.10/tests/integration/window/test_window_caption.py 2019-07-10 21:01:26.000000000 +0000 +++ pyglet-1.5.14/tests/integration/window/test_window_caption.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,5 +1,3 @@ -# encoding: utf-8 - import sys import pyglet @@ -20,7 +18,7 @@ for test_caption in window_captions: # Override sys.argv[0] so that the file name appears to be the caption: - sys.argv[0] = test_caption.encode('utf-8') + sys.argv[0] = test_caption # The window caption should be set to the file name: window = pyglet.window.Window() diff -Nru pyglet-1.4.10/tests/interactive/conftest.py pyglet-1.5.14/tests/interactive/conftest.py --- pyglet-1.4.10/tests/interactive/conftest.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/conftest.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,7 +1,6 @@ """ pytest hooks for interactive test cases. """ -from __future__ import absolute_import import inspect import pytest diff -Nru pyglet-1.4.10/tests/interactive/image/test_image.py pyglet-1.5.14/tests/interactive/image/test_image.py --- pyglet-1.4.10/tests/interactive/image/test_image.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/image/test_image.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,4 +1,3 @@ -from __future__ import division from io import BytesIO import pytest Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.002.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.002.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.003.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.graphics.test_multitexture.test_multitexture.003.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_16bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_16bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_1bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_1bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_24bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_24bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_32bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_32bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_4bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_4bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_8bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgb_8bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgba_32bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_bmp_loading[rgba_32bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_buffer_copy.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_buffer_copy.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_buffer_saving.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_buffer_saving.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_checkerboard.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_checkerboard.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt1.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt1.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt3.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt3.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt5.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgba_dxt5.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgb_dxt1.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_dds_loading[rgb_dxt1.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_depth_buffer_saving.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_depth_buffer_saving.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[8bpp.gif].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[8bpp.gif].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[la.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[la.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[l.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[l.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgba.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgba.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgb.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgb.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[la.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[la.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[l.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[l.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp_trans.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp_trans.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgba.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgba.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_loading[rgb.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[la.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[la.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[l.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[l.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[rgba.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[rgba.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[rgb.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/committed/tests.interactive.image.test_image.test_pypng_saving[rgb.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.002.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.002.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.003.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.graphics.test_multitexture.test_multitexture.003.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_16bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_16bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_1bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_1bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_24bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_24bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_32bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_32bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_4bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_4bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_8bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgb_8bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgba_32bpp.bmp].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_bmp_loading[rgba_32bpp.bmp].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_buffer_copy.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_buffer_copy.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_buffer_saving.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_buffer_saving.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_checkerboard.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_checkerboard.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt1.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt1.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt3.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt3.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt5.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgba_dxt5.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgb_dxt1.dds].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_dds_loading[rgb_dxt1.dds].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_depth_buffer_saving.001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_depth_buffer_saving.001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[8bpp.gif].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[8bpp.gif].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[la.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[la.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[l.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[l.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgba.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgba.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgb.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_gdkpixbuf2_loading[rgb.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[la.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[la.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[l.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[l.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[rgba.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[rgba.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[rgb.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pil_loading[rgb.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[la.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[la.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[l.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[l.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp_trans.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb_8bpp_trans.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgba.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgba.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_loading[rgb.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[la.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[la.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[l.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[l.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[rgba.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[rgba.png].001.png differ Binary files /tmp/tmprXU1Yx/qmj9A1pHdu/pyglet-1.4.10/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[rgb.png].001.png and /tmp/tmprXU1Yx/0OLhcz9082/pyglet-1.5.14/tests/interactive/screenshots/session/tests.interactive.image.test_image.test_pypng_saving[rgb.png].001.png differ diff -Nru pyglet-1.4.10/tests/interactive/text/test_inline_elements.py pyglet-1.5.14/tests/interactive/text/test_inline_elements.py --- pyglet-1.4.10/tests/interactive/text/test_inline_elements.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/text/test_inline_elements.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,4 +1,3 @@ -from builtins import range import pytest from tests.base.interactive import InteractiveTestCase diff -Nru pyglet-1.4.10/tests/interactive/window/test_window_events.py pyglet-1.5.14/tests/interactive/window/test_window_events.py --- pyglet-1.4.10/tests/interactive/window/test_window_events.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/window/test_window_events.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,8 +1,6 @@ """ Tests for events on windows. """ -from __future__ import print_function - import pytest import random diff -Nru pyglet-1.4.10/tests/interactive/window/test_window_fullscreen.py pyglet-1.5.14/tests/interactive/window/test_window_fullscreen.py --- pyglet-1.4.10/tests/interactive/window/test_window_fullscreen.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/window/test_window_fullscreen.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,6 +1,3 @@ -from __future__ import print_function -from __future__ import absolute_import -from builtins import range import pytest from tests.base.interactive import InteractiveTestCase diff -Nru pyglet-1.4.10/tests/interactive/window/test_window_modes.py pyglet-1.5.14/tests/interactive/window/test_window_modes.py --- pyglet-1.4.10/tests/interactive/window/test_window_modes.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/window/test_window_modes.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,5 +1,3 @@ -from __future__ import print_function -from builtins import chr import pytest import time diff -Nru pyglet-1.4.10/tests/interactive/window/test_window_multisample.py pyglet-1.5.14/tests/interactive/window/test_window_multisample.py --- pyglet-1.4.10/tests/interactive/window/test_window_multisample.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/window/test_window_multisample.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,5 +1,3 @@ -from __future__ import print_function -from __future__ import division import pytest from tests.base.interactive import InteractiveTestCase diff -Nru pyglet-1.4.10/tests/interactive/window/test_window_open.py pyglet-1.5.14/tests/interactive/window/test_window_open.py --- pyglet-1.4.10/tests/interactive/window/test_window_open.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/window/test_window_open.py 2020-11-10 10:36:26.000000000 +0000 @@ -5,6 +5,7 @@ from pyglet import window from pyglet.gl import * + @pytest.mark.requires_user_validation class WINDOW_OPEN(InteractiveTestCase): def open_window(self): diff -Nru pyglet-1.4.10/tests/interactive/window/test_window_settings.py pyglet-1.5.14/tests/interactive/window/test_window_settings.py --- pyglet-1.4.10/tests/interactive/window/test_window_settings.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/window/test_window_settings.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,6 +1,4 @@ """Tests for window settings.""" -from __future__ import print_function - import pytest import time diff -Nru pyglet-1.4.10/tests/interactive/window/window_util.py pyglet-1.5.14/tests/interactive/window/window_util.py --- pyglet-1.4.10/tests/interactive/window/window_util.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/interactive/window/window_util.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,8 +1,6 @@ -#!/usr/bin/python -# $Id:$ - from pyglet.gl import * + def draw_client_border(window): glClearColor(0, 0, 0, 1) glClear(GL_COLOR_BUFFER_BIT) @@ -13,6 +11,7 @@ glMatrixMode(GL_MODELVIEW) glLoadIdentity() + def rect(x1, y1, x2, y2): glBegin(GL_LINE_LOOP) glVertex2f(x1, y1) @@ -20,7 +19,7 @@ glVertex2f(x2, y2) glVertex2f(x1, y2) glEnd() - + glColor3f(1, 0, 0) rect(-2, -2, window.width + 2, window.height + 2) diff -Nru pyglet-1.4.10/tests/unit/media/test_listener.py pyglet-1.5.14/tests/unit/media/test_listener.py --- pyglet-1.4.10/tests/unit/media/test_listener.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/unit/media/test_listener.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import pytest from ...annotations import skip_if_continuous_integration diff -Nru pyglet-1.4.10/tests/unit/media/test_player.py pyglet-1.5.14/tests/unit/media/test_player.py --- pyglet-1.4.10/tests/unit/media/test_player.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/unit/media/test_player.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,7 +1,6 @@ -from __future__ import division -from builtins import range -from tests import mock import random +from unittest import mock + from tests.base.future_test import FutureTestCase from pyglet.media.player import Player, PlayerGroup @@ -152,7 +151,7 @@ self.mock_clock.unschedule.assert_called_with(self.player.update_texture) def pretend_player_at_time(self, t): - self.player._mclock.set_time(t) + self.player._timer.set_time(t) def pretend_silent_driver_player_at_time(self, t): self.mock_silent_audio_driver_player.get_time.return_value = t diff -Nru pyglet-1.4.10/tests/unit/media/test_riff.py pyglet-1.5.14/tests/unit/media/test_riff.py --- pyglet-1.4.10/tests/unit/media/test_riff.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/tests/unit/media/test_riff.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -""" -Test the internal RIFF reader. -""" - -import os -import unittest - -from pyglet.media.codecs.wave import WaveSource - -test_data_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'data', 'media')) - - -class RiffTest(unittest.TestCase): - def test_pcm_16_11025_1ch(self): - file_name = os.path.join(test_data_dir, 'alert_pcm_16_11025_1ch.wav') - source = WaveSource(file_name) - - self._check_audio_format(source, 1, 16, 11025) - self._check_data(source, 11584, 0.525) - - def test_pcm_16_22050_1ch(self): - file_name = os.path.join(test_data_dir, 'alert_pcm_16_22050_1ch.wav') - source = WaveSource(file_name) - - self._check_audio_format(source, 1, 16, 22050) - self._check_data(source, 23166, 0.525) - - def test_pcm_8_22050_1ch(self): - file_name = os.path.join(test_data_dir, 'alert_pcm_8_22050_1ch.wav') - source = WaveSource(file_name) - - self._check_audio_format(source, 1, 8, 22050) - self._check_data(source, 11583, 0.525) - - def test_seek(self): - file_name = os.path.join(test_data_dir, 'alert_pcm_16_22050_1ch.wav') - source = WaveSource(file_name) - - seek_time = 0.3 - seek_bytes = seek_time * source.audio_format.bytes_per_second - source.seek(seek_time) - self._check_data(source, 23166-seek_bytes, 0.225) - - def _check_audio_format(self, source, channels, sample_size, sample_rate): - self.assertEqual(channels, source.audio_format.channels) - self.assertEqual(sample_size, source.audio_format.sample_size) - self.assertEqual(sample_rate, source.audio_format.sample_rate) - - def _check_data(self, source, expected_bytes, expected_duration): - received_bytes = 0 - received_seconds = 0. - bytes_to_read = 1024 - - while True: - data = source.get_audio_data(bytes_to_read) - if data is None: - break - - received_bytes += data.length - received_seconds += data.duration - - self.assertEqual(data.length, len(data.data)) - - self.assertAlmostEqual(expected_duration, received_seconds, places=1) - self.assertAlmostEqual(expected_bytes, received_bytes, delta=5) - diff -Nru pyglet-1.4.10/tests/unit/media/test_sources.py pyglet-1.5.14/tests/unit/media/test_sources.py --- pyglet-1.4.10/tests/unit/media/test_sources.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/unit/media/test_sources.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,8 +1,8 @@ -from __future__ import division -from builtins import map -from tests import mock import os +import ctypes import unittest +from unittest import mock + from tests.base.future_test import FutureTestCase import pyglet diff -Nru pyglet-1.4.10/tests/unit/media/test_synthesis.py pyglet-1.5.14/tests/unit/media/test_synthesis.py --- pyglet-1.4.10/tests/unit/media/test_synthesis.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/unit/media/test_synthesis.py 2020-12-23 15:47:40.000000000 +0000 @@ -1,8 +1,7 @@ -from __future__ import absolute_import -from builtins import bytes, object +import unittest + from ctypes import sizeof from io import BytesIO -import unittest from pyglet.media.synthesis import * @@ -20,7 +19,7 @@ return os.path.join(test_data_path, *file_parts) -class SynthesisSourceTest(object): +class SynthesisSourceTest: """Simple test to check if synthesized sources provide data.""" source_class = None diff -Nru pyglet-1.4.10/tests/unit/test_atlas.py pyglet-1.5.14/tests/unit/test_atlas.py --- pyglet-1.4.10/tests/unit/test_atlas.py 2019-10-21 21:56:02.000000000 +0000 +++ pyglet-1.5.14/tests/unit/test_atlas.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,13 +1,11 @@ -#!/usr/bin/python -# $Id:$ - import unittest from pyglet.image import atlas __noninteractive = True -class Rect(object): + +class Rect: def __init__(self, x1, y1, x2, y2): self.x1 = x1 self.y1 = y1 @@ -22,7 +20,8 @@ return self.x2 > other.x1 and self.x1 < other.x2 and \ self.y2 > other.y1 and self.y1 < other.y2 -class AllocatorEnvironment(object): + +class AllocatorEnvironment: def __init__(self, test_case, width, height): self.test_case = test_case self.rectes = [] @@ -46,6 +45,7 @@ self.test_case.assertRaises(atlas.AllocatorException, self.allocator.alloc, width, height) + class TestPack(unittest.TestCase): def test_over_x(self): env = AllocatorEnvironment(self, 3, 3) @@ -87,6 +87,7 @@ env.add(4, 2) env.add(1, 2) env.add_fail(1, 1) - + + if __name__ == '__main__': unittest.main() diff -Nru pyglet-1.4.10/tests/unit/test_clock_fps.py pyglet-1.5.14/tests/unit/test_clock_fps.py --- pyglet-1.4.10/tests/unit/test_clock_fps.py 2020-01-15 20:20:02.000000000 +0000 +++ pyglet-1.5.14/tests/unit/test_clock_fps.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,7 +1,6 @@ """Tests clock timing between frames and estimations of frames per second. """ -from __future__ import absolute_import import time import pytest diff -Nru pyglet-1.4.10/tests/unit/test_events.py pyglet-1.5.14/tests/unit/test_events.py --- pyglet-1.4.10/tests/unit/test_events.py 2019-11-26 14:15:10.000000000 +0000 +++ pyglet-1.5.14/tests/unit/test_events.py 2020-11-10 10:36:26.000000000 +0000 @@ -11,7 +11,7 @@ @pytest.fixture def dispatcher(): - "Event dispatcher to test" + """Event dispatcher to test""" dispatcher = pyglet.event.EventDispatcher() yield dispatcher # Let's remove previous handlers diff -Nru pyglet-1.4.10/tests/unit/test_font.py pyglet-1.5.14/tests/unit/test_font.py --- pyglet-1.4.10/tests/unit/test_font.py 2019-07-10 21:01:26.000000000 +0000 +++ pyglet-1.5.14/tests/unit/test_font.py 2020-11-10 10:36:26.000000000 +0000 @@ -2,10 +2,10 @@ Test pyglet font package """ -import pytest import pyglet from tests.annotations import require_platform, Platform + @require_platform(Platform.WINDOWS) def test_load_privatefont(test_data): @@ -13,10 +13,11 @@ action_man_font = pyglet.font.load("Action Man", size=12, dpi=96) assert action_man_font.logfont.lfFaceName.decode("utf-8") == "Action Man" + @require_platform(Platform.WINDOWS) def test_load_privatefont_from_list(test_data): """Test for Issue #100""" pyglet.font.add_file(test_data.get_file('fonts', 'action_man.ttf')) action_man_font = pyglet.font.load(["Action Man"], size=12, dpi=96) - assert action_man_font.logfont.lfFaceName.decode("utf-8") == "Action Man" \ No newline at end of file + assert action_man_font.logfont.lfFaceName.decode("utf-8") == "Action Man" diff -Nru pyglet-1.4.10/tests/unit/test_math.py pyglet-1.5.14/tests/unit/test_math.py --- pyglet-1.4.10/tests/unit/test_math.py 1970-01-01 00:00:00.000000000 +0000 +++ pyglet-1.5.14/tests/unit/test_math.py 2020-11-18 14:04:10.000000000 +0000 @@ -0,0 +1,115 @@ +import pytest + +from pyglet.math import Mat4 + + +@pytest.fixture() +def mat4(): + return Mat4() + + +################## +# helper functions +################## + +def inverse(matrix): + """The inverse of a Matrix. + Using Gauss-Jordan elimination, the matrix supplied is transformed into + the identity matrix using a sequence of elementary row operations (below). + The same sequence of operations is applied to the identity matrix, + transforming it into the supplied matrix's inverse. + Elementary row operations: + - multiply row by a scalar + - swap two rows + - add two rows together""" + + i = Mat4() # identity matrix + + # The pivot in each column is the element at Matrix[c, c] (diagonal elements). + # The pivot row is the row containing the pivot element. Pivot elements must + # be non-zero. + # Any time matrix is changed, so is i. + for c in range(4): + # Find and swap pivot row into place + if matrix[4*c + c] == 0: + for r in range(c + 1, 4): + if matrix[4*r + c] != 0: + matrix = row_swap(matrix, c, r) + i = row_swap(i, c, r) + + # Make 0's in column for rows that aren't pivot row + for r in range(4): + if r != c: # not the pivot row + r_piv = matrix[4*r + c] + if r_piv != 0: + piv = matrix[4*c + c] + scalar = r_piv / piv + matrix = row_mul(matrix, c, scalar) + matrix = row_sub(matrix, c, r) + i = row_mul(i, c, scalar) + i = row_sub(i, c, r) + + # Put matrix in reduced row-echelon form. + piv = matrix[4*c + c] + matrix = row_mul(matrix, c, 1/piv) + i = row_mul(i, c, 1/piv) + return i + + +def row_swap(matrix, r1, r2): + lo = min(r1, r2) + hi = max(r1, r2) + values = (matrix[:lo*4] + + matrix[hi*4:hi*4 + 4] + + matrix[lo*4 + 4:hi*4] + + matrix[lo*4:lo*4 + 4] + + matrix[hi*4 + 4:]) + return Mat4(values) + + +def row_mul(matrix, r, x): + values = (matrix[:r*4] + + tuple(v * x for v in matrix[r*4:r*4 + 4]) + + matrix[r*4 + 4:]) + return Mat4(values) + + +# subtracts r1 from r2 +def row_sub(matrix, r1, r2): + row1 = matrix[4*r1:4*r1 + 4] + row2 = matrix[4*r2:4*r2 + 4] + values = (matrix[:r2*4] + + tuple(v2 - v1 for (v1, v2) in zip(row1, row2)) + + matrix[r2*4 + 4:]) + return matrix.Mat4(values) + + +################ +# test functions +################ + +def test_creation(mat4): + assert len(mat4) == 16 + assert mat4 == (1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1) + + +def test_creation_from_list(mat4): + mat4_from_list = Mat4([1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1]) + assert mat4_from_list == mat4 + + +def test_matrix_inversion(mat4): + # Confirm `__invert__` method matches long hand utility method: + inverse_1 = inverse(mat4) + inverse_2 = ~mat4 + assert round(inverse_1, 9) == round(inverse_2, 9) + # Confirm that Matrix @ it's inverse == identity Matrix: + assert round(mat4 @ inverse_1, 9) == Mat4() + assert round(mat4 @ inverse_2, 9) == Mat4() + diff -Nru pyglet-1.4.10/tests/unit/test_resource_path.py pyglet-1.5.14/tests/unit/test_resource_path.py --- pyglet-1.4.10/tests/unit/test_resource_path.py 2019-07-10 21:01:26.000000000 +0000 +++ pyglet-1.5.14/tests/unit/test_resource_path.py 2020-11-10 10:36:26.000000000 +0000 @@ -1,8 +1,8 @@ import os import re import sys -from tests import mock import unittest +from unittest import mock import pyglet from pyglet.resource import get_script_home, get_settings_path